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 标签的数量

参考链接

相关推荐
雷渊几秒前
深入分析dubbo的优雅停机
后端
Whbbit19991 分钟前
hono 集成 Better Auth
javascript·后端
雷渊14 分钟前
聊一聊Dubbo中的泛化调用
后端
雷渊29 分钟前
深入分析RPC和HTTP的区别
后端
极客智谷40 分钟前
Spring AI应用系列——基于DashScope平台自主Model配置的Chat应用
人工智能·后端
用户4099322502121 小时前
FastAPI数据库连接池配置与监控
后端·ai编程·trae
嘻嘻嘻嘻嘻嘻ys1 小时前
《Spring Boot 3响应式架构实战:R2DBC驱动的高并发数据持久化革命》
前端·后端
小镇cxy1 小时前
Java开发,实践MCP
后端
木昜先生1 小时前
知识点:深入理解 JVM 内存管理与垃圾回收
java·jvm·后端
115432031q1 小时前
基于SpringBoot+Vue实现的旅游景点预约平台功能十三
java·前端·后端