围绕单个 rid 串起证据链#

扩展与调试这一节前面已经分别讲了 RequestStage / ReqTimeStats、request logger、request metrics exporter、crash dump / straggler 和 trace,但维护者在现场排障时真正面对的,往往不是“某一类工具怎么用”,而是更具体的问题:我现在已经锁定一个 rid,怎样把它在不同证据系统里的痕迹串起来,形成一条完整故事线?

这章的价值正在这里。它不再介绍单个工具,而是教你怎样把多种局部证据真正拼成一次有效排障。只要这条拼接顺序稳住了,前面那些工具章节才会真正从“看过”变成“能用”。

先接受一个事实:单条请求的真相天然分散在多条证据链上#

很多人会下意识希望“拿到 rid 就能在某个地方看到完整真相”。真实系统不是这样。单条请求的完整故事往往天然分布在多种证据面里:

flowchart LR
    RID["rid"] --> Stage["ReqTimeStats / RequestStage"]
    RID --> Log["RequestLogger"]
    RID --> Export["request metrics exporter"]
    RID --> Dump["dump / crash dump / straggler"]
    RID --> Trace["trace / spans / events"]

这张图最重要的不是“有五个工具”,而是它提醒读者:排障的关键不是找到唯一真相入口,而是知道该按什么顺序把这些局部证据拼起来。

第一步先做 identity 对齐,而不是直接开 trace#

更稳的起手式永远是先确认 request identity 有没有对准。你要先确认:

  • 这条请求在协议层、内部对象层和回包层是不是同一个 rid
  • 如果它来自 batch / parallel sample,子请求 rid 是否已经映射正确
  • 如果是 streaming,多次 chunk 是否仍然指向同一内部请求

这一步如果没立住,后面再细的 trace、dump 和 exporter 记录都可能串错对象。很多排障效率低,不是因为工具不够,而是 request identity 在第一步就已经偏了。

第二步再用阶段切片决定“卡在哪”#

rid 对齐之后,下一步最该看的是 RequestStage / ReqTimeStats。原因很简单:它们最适合回答“卡在哪一段”,而不是“发生了多少细节”。

围绕单个请求,最有价值的判断通常是:

  • 卡在 TOKENIZE
  • 卡在 API_SERVER_DISPATCH
  • 卡在 PREFILL_WAITING
  • 卡在 DECODE_WAITING
  • 还是卡在更特殊的 bootstrap / transfer / retract 阶段

这一层的价值,是先把问题从“很多工具名”压成“流程哪一段有问题”。如果这一步没做,你很容易直接掉进全量 trace 或全量日志里,看到很多信息,却没有稳定方向。

第三步用 request logger 先站稳首尾事实#

当你已经知道大致卡在哪一层之后,再回头看 request logger,效率会高得多。因为 logger 最擅长回答的不是全部细节,而是这条请求的首尾事实:

  • 什么时候进入系统
  • 最终以什么 finish reason 结束
  • 参数和结果摘要是什么

这一步相当于替单条请求补一条外部故事线。然后你再去 trace、dump 或 exporter 里找更细的局部证据时,就不会不断怀疑“我是不是已经看错对象了”。

exporter 的价值,不在于替代 logger,而在于让单个异常请求进入可比较空间#

request metrics exporter 更适合做结构化比对,例如:

  • 和其他异常请求做批量对比
  • 看同一类请求的 finish / latency 差异
  • 把单个异常 request 嵌回更大统计样本里

这层的关键不是“它记录得更详细”,而是它把人类可读事实变成了可比较事实。logger 让你先知道这条请求发生了什么;exporter 则让你判断,这是不是一类请求的问题,而不是偶发个案。

dump 系列容器最适合处理“logger 和 trace 还不够”的那部分现实#

单个 rid 相关的 dump 容器通常特别适合以下几种场景:

  • 它正好位于 crash 前后
  • 它是明显的 straggler
  • 它不是每次都会出问题,但总在某个时间窗里出现

这时:

  • dump_request_list 更像周期性快照
  • crash_dump_request_list 更像事故窗口
  • straggler_request_list 更像尾延迟问题的专门容器

这说明围绕单个 rid 的排障并不总是“logger + trace 就够了”。很多偶发问题恰恰要靠这些更偏窗口化的容器补证据。

最后再进 trace,看的是“阶段内部到底发生了什么”#

当你已经知道:

  • 这是哪条请求
  • 它大致卡在哪层
  • 它的首尾事实是什么

这时再去看 trace,信息价值会显著提高。因为 trace 最擅长解释的是阶段内部的细时序,而不是帮你先完成 request identity 对齐。

也正因为这样,把 trace 放在最后,并不是贬低它,而是把它放到最适合发挥价值的位置。对维护者来说,这种顺序比“先开最强工具”更稳。

这条工作流怎样和整本书前文形成闭环#

这章其实是在把整本书后半程的几条主线重新压成一条实战顺序:

  • request lifecycle 提供 identity 和主链
  • ReqTimeStats 提供阶段切片
  • request logger 提供首尾事实
  • exporter 提供结构化落盘
  • dump 提供异常样本
  • trace 提供时序细节

也就是说,这章本身就是“如何把前文这些工具真正一起用起来”。这也是它比单独工具说明更像书的地方。

最容易出现的三种误判#

第一,一上来就看 trace。
如果 rid 还没对准,这通常只会让你更快淹没在细节里。

第二,只看 logger 就下结论。
logger 更像首尾摘要,不足以替代阶段切片和时序细节。

第三,觉得 exporter / dump 只是可有可无。
很多偶发问题恰恰要靠这两类更结构化或更窗口化的证据补齐。

一个更稳的单 rid 排障顺序#

如果要把这章压成最小工作流,建议就是:

  1. 先确认 rid 和子请求映射是否正确。
  2. 再看 RequestStage / ReqTimeStats
  3. 再看 request logger 的首尾事实。
  4. 再看 request metrics exporter 记录。
  5. 再看 crash / straggler / dump 容器。
  6. 最后再进 trace 看细时序。

这条顺序最关键的价值,在于它先缩小问题空间,再增加证据密度,而不是一开始就把所有证据面同时打开。

小结#

围绕单个 rid 串证据链,真正补齐的是维护层里一项非常关键的能力:不是每个工具各用各的,而是知道怎样把它们围绕同一条请求重新组织成一次完整排障。

到这里,第 8 节就更不像一组工具说明,而更像一本成熟系统书最后应有的操作层。