Repository navigation
fix: record RPC metrics synchronously to prevent event loss - #3805
Conversation
Add a recorder callback independent of best-effort channel subscriptions. Cover concurrent publication, full channels and recorder removal. Fixes: apache#3802 Signed-off-by: Kevin <yaojiaangyao@gmail.com>
Install the recorder before Metrics.Init returns, cache complete label identities and bind result series lazily. Preserve readiness snapshots, monitoring expiry and legacy registry behavior. Isolate registry panics from business invocations. Fixes: apache#3802 Signed-off-by: Kevin <yaojiaangyao@gmail.com>
Exercise the real one-time initialization lifecycle, disabled metrics, full observer queues and custom registry failures. Verify returned errors and real Exchange callbacks remain intact. Fixes: apache#3802 Signed-off-by: Kevin <yaojiaangyao@gmail.com>
Document readiness, concurrent collection, registry compatibility and recording limits, with commands for the regression suite. Fixes: apache#3802 Signed-off-by: Kevin <yaojiaangyao@gmail.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #3805 +/- ##
===========================================
+ Coverage 58.70% 58.91% +0.20%
===========================================
Files 492 493 +1
Lines 40377 40495 +118
===========================================
+ Hits 23702 23856 +154
+ Misses 14901 14866 -35
+ Partials 1774 1773 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Extract initialization, registry-failure callbacks, concurrent recording and metric assertions into focused test helpers. Preserve the existing scenarios, concurrency levels and assertions while reducing cognitive complexity. Fixes: apache#3802 Signed-off-by: Kevin <yaojiaangyao@gmail.com>
| } | ||
| return | ||
| } | ||
| c.getSeries(e).record(e) |
There was a problem hiding this comment.
[P2] 新的同步记账入口没有承接 RT 直方图记录,与 #3791 合并后 Prometheus 部署的直方图会停止采样
触发条件:#3791 在 afterInvokeHandler 末尾新增 c.reportRTHistogram(role, labels, event.costTime.Seconds())(该 PR head 221c96e2 的 metrics/rpc/collector.go:127),并在 newRPCCollector 注入 histogramRegistry。本 PR 删除了 rpcMetricsChan 与 go rc.start(),afterInvokeHandler 只在「注册表未实现 GaugeFuncRegistry」的兼容分支内被调用(metrics/rpc/collector.go:81-91,metrics/api.go:343 是同一判据的另一处)。promMetricRegistry 实现了 GaugeFunc(metrics/prometheus/registry.go:96),因此默认 Prometheus 部署的每个完成事件都走本行的 c.getSeries(e).record(e),reportRTHistogram 不再可达。
实际影响:两个 PR 都合入后,dubbo_provider_rt_milliseconds_histogram_seconds / dubbo_consumer_rt_milliseconds_histogram_seconds 只会暴露空序列(_bucket 各档位、_sum、_count 恒为 0),开启 histogram.enabled=true 的部署拿不到任何延迟分布数据;既有 RT、计数与 processing 指标不受影响。若按相反顺序合并(本 PR 先入、#3791 后 rebase),结论相同,因为新入口是本 PR 引入的。
证据:本 PR head 803bd2cd 的 record() 里,BeforeInvoke/ObservationExpired/AfterInvoke 三个分支均只更新 qps、processing、counter、rt、quantiles,没有任何 HistogramWithOptions(...).Observe(...) 调用;histogram 相关字段也未出现在本 PR 的 rpcCollector 结构体中。
修复方向:在 rpcSeries.record 的 AfterInvoke 分支中同样观察直方图(等价于把 reportRTHistogram 提到 record() 的分支判断之上,用 e.costTime.Seconds()),并补一条断言:完成事件记录后 _bucket 的累计值随观测数增长,而非恒为 0。合并顺序上建议先合 #3791,本 PR rebase 时一并迁移该调用点。
There was a problem hiding this comment.
[P2] 新的同步记账入口没有承接 RT 直方图记录,与 #3791 合并后 Prometheus 部署的直方图会停止采样
触发条件:#3791 在
afterInvokeHandler末尾新增c.reportRTHistogram(role, labels, event.costTime.Seconds())(该 PR head221c96e2的metrics/rpc/collector.go:127),并在newRPCCollector注入histogramRegistry。本 PR 删除了rpcMetricsChan与go rc.start(),afterInvokeHandler只在「注册表未实现GaugeFuncRegistry」的兼容分支内被调用(metrics/rpc/collector.go:81-91,metrics/api.go:343是同一判据的另一处)。promMetricRegistry实现了GaugeFunc(metrics/prometheus/registry.go:96),因此默认 Prometheus 部署的每个完成事件都走本行的c.getSeries(e).record(e),reportRTHistogram不再可达。实际影响:两个 PR 都合入后,
dubbo_provider_rt_milliseconds_histogram_seconds/dubbo_consumer_rt_milliseconds_histogram_seconds只会暴露空序列(_bucket各档位、_sum、_count恒为 0),开启histogram.enabled=true的部署拿不到任何延迟分布数据;既有 RT、计数与processing指标不受影响。若按相反顺序合并(本 PR 先入、#3791 后 rebase),结论相同,因为新入口是本 PR 引入的。证据:本 PR head
803bd2cd的record()里,BeforeInvoke/ObservationExpired/AfterInvoke三个分支均只更新qps、processing、counter、rt、quantiles,没有任何HistogramWithOptions(...).Observe(...)调用;histogram相关字段也未出现在本 PR 的rpcCollector结构体中。修复方向:在
rpcSeries.record的AfterInvoke分支中同样观察直方图(等价于把reportRTHistogram提到record()的分支判断之上,用e.costTime.Seconds()),并补一条断言:完成事件记录后_bucket的累计值随观测数增长,而非恒为 0。合并顺序上建议先合 #3791,本 PR rebase 时一并迁移该调用点。
明白
|



Problem
目前 RPC 指标依赖容量为 1,024 的异步 channel 消费更新。高并发打满队列时,请求开始、完成或过期事件会直接丢弃,导致计数和 RT 样本缺失、
processing指标无法归零;此外,刚初始化完成时的首批请求也可能早于异步订阅就绪,造成漏记。Changes
Metrics.Init返回前注册同步 RPC recorder。Publish时直接在当前 goroutine 完成核心指标更新,之后再尝试向异步订阅者发送事件通知。processing,后续晚到的响应不再重复统计。GaugeFuncRegistry的自定义 Registry,保留原有的串行兼容路径;捕获 Registry panic,避免指标异常影响业务结果或阻断用户回调(注:慢 Registry 仍会直接增加调用或回调耗时)。Behavior
Publish返回前同步更新,异步通道满仅影响外部通知订阅者(注:Registry 自身故障或进程崩溃仍无法保证记账完整)。Verification
race、vet及定向 lint 检查均已通过。processing最终归零;RT 探针、Prometheus 实际采集、跨时间窗口及 30 分钟持续负载核账全部通过。go.mod和go.sum与集群实测版本完全一致;未覆盖场景及已知协议层 race 见折叠详情)。Review focus
SubscribeRecorder接口,以及自定义 Registry 的并发与异常兜底约定是否合适。Details & Attachments:关联 Issue、验证报告与实现图解
关联 Issue
Fixes #3802
验证报告与实现图解
版本与测试范围说明
6b04ac04bfc868060995dc9fa4e1dcc5138ad02b803bd2cda9530362192e911dc4a4492d11f5e883f345d2ace82606b79e5bc209717b16648e3b791c(整理提交时仅移除了实验脚本并补充了测试与文档,框架生产代码与实测版本一致,故当前 HEAD 未重复上集群)。race和vet;2026-11-12 在 Windows Go 1.26.5 下重跑了同范围常规测试并全部通过。改动文件已通过gofmt、modernize v0.21.1、仓库imports-formatter以及golangci-lint v2.7.2(0 issues),未跑全仓 CI。E2E 与性能摘要
processing归零;基线版本每轮均有漏记且processing出现偏移。processing归零。已知边界与未覆盖项
race及 19,197 次跨节点race均通过;额外跑的 Getty / Hessian 协议层race在基线和修复版上均失败(涉及连接清理逻辑),不计入通过范围。Checklist
developmake fmtto format my code(已完成改动文件的定向格式化检查,未执行全仓改写)make testto run local tests(已跑受影响包,未跑全仓测试)