Quarkus响应式端点@Timed注解计时与默认HTTP计时不一致问题
问题描述
我有一个JAX-RS端点代码如下:
@Path("/data") @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Timed("rest.request.timer") public Multi<***> getData(***) { // 业务逻辑 return x; }
Micrometer默认会对HTTP请求生成计时指标http_server_requests_seconds_sum,这个指标能正确反映请求的完整响应耗时。但我在方法上添加@Timed注解自定义的rest.request.timer指标,数值和默认指标不一致。
我还尝试了手动计时的方式,代码如下:
Timer timer = Timer.builder("rest.request.timer").register(Metrics.globalRegistry); public Uni<***> getData(* request) throws IOException { return this.timer.record(() -> { return serviceObject.get(); }); }
结果自定义指标的数值还是和默认指标不一致,请问这是什么原因?
原因分析与解决方案
核心原因是默认HTTP计时和自定义计时的生命周期覆盖范围完全不同:
1. 默认指标http_server_requests_seconds_sum的计时范围
它会覆盖HTTP请求的全生命周期:从请求到达服务器开始,经过请求解码、业务方法执行、响应编码,直到整个响应(包括Multi流式返回的所有元素)完全发送给客户端才停止计时,是真正的端到端请求耗时。
2. 自定义计时的范围缺陷
- 直接用
@Timed标注返回Multi/Uni的方法:Micrometer默认只会计时到方法返回响应式对象(Multi/Uni)的时刻,而后续异步执行的流式数据处理、响应发送过程不会被计入,导致计时偏短。 - 手动用
timer.record()包裹serviceObject.get():仅计时了业务方法本身的耗时,没有包含请求解码、响应编码以及Multi后续流式传输的时间,自然和默认指标的总耗时不符。
正确的自定义实现方式
方式1:使用Quarkus增强的@Timed配置
Quarkus对Micrometer的@Timed做了扩展,通过recordOnComplete参数可以让计时覆盖响应式流的完整生命周期:
@Path("/data") @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Timed(value = "rest.request.timer", recordOnComplete = true) public Multi<***> getData(***) { // 业务逻辑 return x; }
recordOnComplete = true会让计时器等到Multi的所有元素处理完成、响应完全发送后才停止,和默认HTTP计时的范围对齐。
方式2:手动计时覆盖完整响应流
如果采用手动计时,需要确保计时器包裹住Multi的整个执行过程:
Timer timer = Timer.builder("rest.request.timer").register(Metrics.globalRegistry); public Multi<***> getData(* request) throws IOException { return serviceObject.get() .transform(multi -> timer.record(multi)); }
针对Uni场景:
public Uni<***> getData(* request) throws IOException { return timer.recordCallable(serviceObject::get); }
内容的提问来源于stack exchange,提问作者Jaiprasad
相关产品推荐
相关产品推荐

