同时呢,还可以搭配 @io.micrometer.core.annotation.Timer 注解,订制监控并且增加 Histogram,例如:
//@Timer 注解想生效需要注册一个 io.micrometer.core.aop.TimedAspect Bean 并且启用切面 @Bean public TimedAspect timedAspect(MeterRegistry registry) { return new TimedAspect(registry); } @Timed(histogram=true) @RequestMapping("/query/orders") public xxxx xxxx() { ..... }这样就会除了上面的数据额外得到类似于 bucket 的统计数据:
http_server_requests_seconds_bucket{exception="None",method="GET",outcome="SUCCESS",status="200",uri="/facts-center/query/frontend/market-info",le="0.001",} 0.0 http_server_requests_seconds_bucket{exception="None",method="GET",outcome="SUCCESS",status="200",uri="/facts-center/query/frontend/market-info",le="0.001048576",} 0.0 http_server_requests_seconds_bucket{exception="None",method="GET",outcome="SUCCESS",status="200",uri="/facts-center/query/frontend/market-info",le="0.001398101",} 0.0 http_server_requests_seconds_bucket{exception="None",method="GET",outcome="SUCCESS",status="200",uri="/facts-center/query/frontend/market-info",le="0.001747626",} 0.0 //省略中间的时间层级 http_server_requests_seconds_bucket{exception="None",method="GET",outcome="SUCCESS",status="200",uri="/facts-center/query/frontend/market-info",le="30.0",} 1.0 http_server_requests_seconds_bucket{exception="None",method="GET",outcome="SUCCESS",status="200",uri="/facts-center/query/frontend/market-info",le="+Inf",} 1.0以上这些统计给我们分析问题带来了如下不便的地方:
采集分析压力过大:我们采用了 grafana 采集 prometheus 上报的指标数据,grafana 的时序数据库会将采集到的数据全部保存。自带的 http 监控指标过多,一个路径,一个结果,一个异常,一个方法就有一个特定指标,如果是有将参数作为路径参数的接口,那么这个指标就更多更多了,例如将 userId 放入路径中。我们其实只关注出问题的时间段的请求状况,但是我们并不能预测啥时候出问题,也就无法按需提取,只能一直采集并保存,这也就导致压力过大。
指标对于压力不敏感,无法很准确的用指标进行报警:由于指标并不是采集后就清空,而是从程序开始就一直采集。所以随着程序的运行,这些指标对于瞬时压力的表现波动越来越小。
所以,我们基本不会通过这个指标进行问题定位,也就没必要开启了,于是我们禁用这个 http 请求响应采集,目前没有很优雅的方式单独禁用,只能通过自动扫描注解中排除,例如:
@SpringBootApplication( scanBasePackages = {"com.test"} //关闭 prometheus 的 http request 统计,我们用不到 , exclude = WebMvcMetricsAutoConfiguration.class )首先,JFR 采集是进程内的,并且 JVM 做了很多优化,性能消耗很小,可以指定保留多少天或者保留最多多大的 JFR 记录(保存在本地临时目录),我们可以随用随取。
并且,我们可以将我们感兴趣的信息放入 JFR 事件,作比较灵活的定制。
对于某个请求时间过长一直没有响应的,我们可以分为收到请求和请求响应两个 JFR 事件。
我们来定义这两个 JFR 事件,一个是收到请求的事件,另一个是请求响应的事件:
HttpRequestReceivedJFREvent.java
package com.github.hashjang.spring.cloud.iiford.spring.cloud.webmvc.undertow.jfr; import jdk.jfr.Category; import jdk.jfr.Event; import jdk.jfr.Label; import jdk.jfr.StackTrace; import javax.servlet.ServletRequest; @Category({"Http Request"}) @Label("Http Request Received") @StackTrace(false) public class HttpRequestReceivedJFREvent extends Event { //请求的 traceId,来自于 sleuth private final String traceId; //请求的 spanId,来自于 sleuth private final String spanId; public HttpRequestReceivedJFREvent(ServletRequest servletRequest, String traceId, String spanId) { this.traceId = traceId; this.spanId = spanId; } }