Tracer Version(s)
1.65.4-ext~d08568f425,Guance 扩展分支,基线提交 d08568f425。
Java Version(s)
修正后的真实 javaagent 验证环境:Java 21.0.3,Netty 4.1.111.Final。现场 JVM 厂商和版本未确认。
Bug Report
Netty SSE 客户端的 extension_data.metrics.stream.first_chunk.ms 经常为 0,无法表示请求发出后等待首块正文的耗时。
原实现收到 HttpResponse 响应头后才开始计时,收到任意 HttpContent 就记录首块,并通过整数毫秒转换截断亚毫秒精度。响应头与正文同批到达时,即使服务端在响应前等待了较长时间,记录值也可能为 0;空正文结束标记也会被误算为首块。
涉及 netty-4.1 的 HttpClientRequestTracingHandler、HttpClientResponseTracingHandler 和 NettyClientResponseStream。
Expected Behavior
采用“请求到首块耗时”:
- 起点:Netty HTTP 请求采集 handler 开始处理并创建请求 span。
- 终点:响应采集 handler 收到第一个非空
HttpContent。
- 公式:
(firstReadableContentNanos - requestStartNanos) / 1_000_000.0,使用单调时钟,毫秒保留小数,字段名保持不变。
- 空正文块不触发首块、不增加
stream.chunk_count;没有非空正文或请求起点不可用时,首块字段缺失,不填 0。
- 100/103 临时响应保留原请求起点;最终响应、异常或断连时清理状态,连接复用使用新起点。
首块是 HTTP 正文块,非空 SSE 心跳或注释也算;不等于首个完整 SSE 事件或首 token。起点位于 Netty 请求处理阶段,不保证包含上层连接池排队、DNS 和建连耗时。
Reproduction Code / Steps
- 使用 Netty
HttpClientCodec 发出 HTTP 请求。
- 服务端先等待,例如 120ms。
- 同批发送下面的 SSE 响应头、非空正文及空结束标记。
- 检查
netty.client.stream span 的首块指标:应包含响应头之前的等待;stream.chunk_count 应为 1。
HTTP/1.1 200 OK
Content-Type: text/event-stream
Transfer-Encoding: chunked
d
data: first
0
响应头和 chunk framing 使用 CRLF,data: first 后包含两个 LF(13 字节正文)。
另以 Content-Length: 0 或仅空 chunk 结束的 SSE 响应验证:首块字段应缺失,chunk_count 为 0。
兼容性与验证
同名指标从“响应头到首块”改为“请求开始到首块”,历史数据应按 SDK 版本区分;chunk_count 改为仅统计非空 Netty 正文块。stream span 仍从响应头开始,其 duration 与首块指标起点不同,首块指标可能大于 duration。无需新增配置,替换 javaagent 并重启即可使用。
修正代码新增 12 项回归测试,全部通过;真实 TCP + javaagent + MessagePack 接收验证中,服务端等待 120ms,首块指标为 223.311419ms、chunk_count 为 1,浮点精度保留。该数值包含客户端处理及调度时间,不等于纯服务端等待。
在 d08568f425 基线上的修正分支运行全模块测试:220 项,68 项失败,32 项跳过;60 项为扩展标签断言,8 项为服务端正文采集/阻断断言。原始基线已复现抽查的扩展标签失败及全部 8 项正文失败,未逐一复跑全部 60 项标签失败。上述全模块结果来自该基线,不代表最新 guance 分支的测试结果。
修正 PR
关联草稿 PR:#168(目标分支 guance)。
Tracer Version(s)
1.65.4-ext~d08568f425,Guance 扩展分支,基线提交d08568f425。Java Version(s)
修正后的真实 javaagent 验证环境:Java 21.0.3,Netty 4.1.111.Final。现场 JVM 厂商和版本未确认。
Bug Report
Netty SSE 客户端的
extension_data.metrics.stream.first_chunk.ms经常为 0,无法表示请求发出后等待首块正文的耗时。原实现收到
HttpResponse响应头后才开始计时,收到任意HttpContent就记录首块,并通过整数毫秒转换截断亚毫秒精度。响应头与正文同批到达时,即使服务端在响应前等待了较长时间,记录值也可能为 0;空正文结束标记也会被误算为首块。涉及
netty-4.1的HttpClientRequestTracingHandler、HttpClientResponseTracingHandler和NettyClientResponseStream。Expected Behavior
采用“请求到首块耗时”:
HttpContent。(firstReadableContentNanos - requestStartNanos) / 1_000_000.0,使用单调时钟,毫秒保留小数,字段名保持不变。stream.chunk_count;没有非空正文或请求起点不可用时,首块字段缺失,不填 0。首块是 HTTP 正文块,非空 SSE 心跳或注释也算;不等于首个完整 SSE 事件或首 token。起点位于 Netty 请求处理阶段,不保证包含上层连接池排队、DNS 和建连耗时。
Reproduction Code / Steps
HttpClientCodec发出 HTTP 请求。netty.client.streamspan 的首块指标:应包含响应头之前的等待;stream.chunk_count应为 1。响应头和 chunk framing 使用 CRLF,
data: first后包含两个 LF(13 字节正文)。另以
Content-Length: 0或仅空 chunk 结束的 SSE 响应验证:首块字段应缺失,chunk_count 为 0。兼容性与验证
同名指标从“响应头到首块”改为“请求开始到首块”,历史数据应按 SDK 版本区分;chunk_count 改为仅统计非空 Netty 正文块。stream span 仍从响应头开始,其 duration 与首块指标起点不同,首块指标可能大于 duration。无需新增配置,替换 javaagent 并重启即可使用。
修正代码新增 12 项回归测试,全部通过;真实 TCP + javaagent + MessagePack 接收验证中,服务端等待 120ms,首块指标为 223.311419ms、chunk_count 为 1,浮点精度保留。该数值包含客户端处理及调度时间,不等于纯服务端等待。
在
d08568f425基线上的修正分支运行全模块测试:220 项,68 项失败,32 项跳过;60 项为扩展标签断言,8 项为服务端正文采集/阻断断言。原始基线已复现抽查的扩展标签失败及全部 8 项正文失败,未逐一复跑全部 60 项标签失败。上述全模块结果来自该基线,不代表最新 guance 分支的测试结果。修正 PR
关联草稿 PR:#168(目标分支
guance)。