uri标签导致http.client.requests指标膨胀问题

从一段告警日志开始

js 复制代码
2025-03-28 07:45:55.798  WARN - [http-epoll-1] s.b.a.a.m.OnlyOnceLoggingDenyMeterFilter :
Reached the maximum number of URI tags for 'xxx.http.client.requests'.
Are you using 'uriVariables'?

这段告警日志表示在收集 http.client.requests 指标时,URI 标签的数量达到了最大限制。 那 http.client.requests 指标是指啥呢?

我们知道Spring Boot Actuator 通过 Micrometer 采集和报告服务程序的指标,其中接口相关的指标主要有两种:http.client.requestshttp.server.requests,两者的区别是:

  • http.client.requests指标用于监控发往外部 HTTP 服务的请求,即客户端发起的请求,适用于追踪该服务作为客户端时对其他服务(如 REST API)的调用
  • http.server.requests指标用于监控接收到的 HTTP 请求,即服务器处理的请求,适用于监控该服务作为服务器时接收的请求。

可以理解为http.server.requests是该服务应用程序提供出去的接口的监控指标,http.client.requests是该服务应用程序调第三方接口的监控指标。

为什么会导致这段日志呢?

假设应用程序在请求一个第三方接口,接口路径是/users/{userId},那么当userId传不同值时http.client.requests监控指标就会采集不同的接口请求uri,比如/users/123,/users/124,/users/124,Grafana上的展示结果如图所示:

带来的问题

已经知道这是因为uri tag标签不规范导致采集http.client.requests指标呈现指数级膨胀,但对于接口监控来说并不需要这么多无用的请求uri。那么会有什么样的后果呢?

  • 应用程序会有潜在的OOM问题:http.client.requests监控指标是通过/actuator/metrics接口访问得到的,这意味这这些指标结果是存放在应用程序内存里的,在源码里是存放在map结构,如果放任不管就会导致内存中这个map结构数据越来越大,从而诱发内存OOM
  • 我们知道http.client.requests指标会被prometheus采集存放在数据库里,在Grafana通过prometheus SQL可以查询展示结果,如果prometheus数据库存放了大量这种类型的无用数据,就会拖垮prometheus数据库性能,导致Grafana查询变慢

第一个问题出现的概率比较低,因为spring boot actuator 自己提供了保护机制,对于默认情况,tags 在同一个 metric 下,最多只有 100 个,你也可以在应用程序的application.yaml文件里加上以下配置限制生成的uri tag的大小。

yaml 复制代码
management: 
  metrics: 
    web: 
      server: 
        max-uri-tags: 500

但第二个问题在生产中却很常见,对于自己的应用程序应该负责规范其监控指标,prometheus是公共资源,如果不规范这是对公共资源的浪费,会给运维同事带来困扰

如何解决

这里分情况给出解决方案

情况一:不需要这个http.client.requests指标的话可以考虑在配置文件里禁止这个指标的生成和采集,这里有两种方法:

  • 在配置文件删除或注释指标定义
js 复制代码
management.metrics.web.client.request.metric-name: xxx.http.client.requests

xxx.http.client.requests是这行配置定义的,只要删掉就不会再返回

  • 设置autotime为false
js 复制代码
management.metrics.web.client.request.autotime.enabled=false

详细情况可以参考这个github issue,# Clarify documentation on disabling web client request metrics 注意一点的是,management.metrics.enable.http.client.requests=false并不能禁止这个指标的生成,这点我亲自验证过了

http.client.requests 指标采用延迟初始化的方式,只有访问接口之后,才会初始化对应 uri tag 的指标。也就是说如果你没有访问接口,去 prometheus 里面查指标是查不到的。你可以通过调/actuator/metrics和/actuator/prometheus接口返回查看是否有http.client.requests

情况二:需要看到应用程序调用第三方接口的性能耗时监控,这时http.client.requests指标就不能禁掉了。有两种方法可以避免uri tag参数导致的指标膨胀:

  • 在Webclient类里构造URI template
    导致http.client.requests的uri指标膨胀的情况有两种,一种是参数变量在path,一种是在query里,这两种情况都可以在Webclient类里构造URI template解决。
js 复制代码
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

public class UserClient {

    private final WebClient webClient;

    public UserClient(WebClient.Builder webClientBuilder) {
        this.webClient = webClientBuilder.baseUrl("http://example.com/api").build();
    }

    // 使用路径变量
    public Mono<User> getUserById(String userId) {
        String uriTemplate = "/users/{userId}"; // 路径变量
        return webClient.get()
                .uri(uriTemplate, userId) // 传递路径变量
                .retrieve()
                .bodyToMono(User.class);
    }

    // 使用查询参数
    public Mono<User> getUserByQuery(String userId) {
        String uriTemplate = "/users?userId={userId}"; // 查询参数
        return webClient.get()
                .uri(uriTemplate, userId) // 传递查询参数
                .retrieve()
                .bodyToMono(User.class);
    }
}
  • 重写WebClientCustomizer接口

WebClientCustomizer 用于自定义 WebClient 的配置。通过实现 WebClientCustomizer 接口,你可以在创建 WebClient 实例时对其进行定制,比如添加拦截器、设置默认的请求头、配置连接池等。 通过重写 WebClientCustomizer 接口并实现自定义的 URI 替换,可以在 http.client.requests 指标中减少 URI 标签的数量

参考链接

相关推荐
[email protected]5 分钟前
ASP.NET Core Web API 参数传递方式
后端·asp.net·.netcore
秋野酱7 分钟前
基于SpringBoot酒店管理系统设计和实现(源码+文档+部署讲解)
java·spring boot·后端
Asthenia041237 分钟前
面试复盘:深入剖析 IOC 容器
后端
ChinaRainbowSea2 小时前
8. RabbitMQ 消息队列 + 结合配合 Spring Boot 框架实现 “发布确认” 的功能
java·spring boot·分布式·后端·rabbitmq·java-rabbitmq
星星电灯猴2 小时前
flutter: 解析 Bloc 实现原理
后端
bcbnb2 小时前
Flutter_bloc框架使用笔记,后续估计都不太会用了(1)
后端
唐静蕴2 小时前
Kotlin语言的安全开发
开发语言·后端·golang
调试人生的显微镜2 小时前
Flutter开发 -- 使用Bloc管理状态
后端
开心猴爷2 小时前
深入解析 Flutter Bloc:从原理到实战
后端
aiopencode3 小时前
Flutter中的BLoC,你所需要知道的一切
后端