最难查的故障不是报错的那种。报错的故障有堆栈、有错误码、有一句话告诉你哪里不对。
最难查的是这种:上游说「我发了」,下游说「我没收到」,中间的每一个环节都说「我正常」。 日志翻三遍,没有一行是红的,但产物就是没有。
过去两年我在这类故障上花的时间,比所有报错故障加起来还多。下面是我最后摸出来的一套做法,不保证通用,但每次都管用。
第一步:别看日志,先画时间线
日志是按组件分的——A 的日志、B 的日志、C 的日志。而这类故障几乎都发生在组件之间,也就是日志覆盖不到的地方。
所以我第一件事不是翻日志,是把所有组件的关键事件按绝对时间抄到同一张纸上:A 在几点几分几秒发出、B 在几点几分几秒收到、B 几点开始处理、几点回应……
画到一半,问题常常就自己浮出来了。上一次是这样的:A 发出后 4.8 秒收到 B 的确认,但 B 的日志显示自己在 A 发出后 5 秒才开始处理。确认比处理早——那个确认是谁发的? 顺着这条线查下去,是中间一层代理在替 B 应答,然后自己把请求丢了。
单看任何一方的日志,这件事都是「正常」的。
第二步:拿报文原文对照,不要凭印象
第二步最反直觉:不要相信自己对代码的理解,去看实际发出去的东西。
有一次我断定问题出在请求格式上,因为「我记得那里多带了一个端口号」。改了、重试、没用。最后把两边真实的报文原文摆在一起,发现端口号一直都在,打通的那几次也带着——我改掉的是一个从来就没坏的地方,还把它改坏了。
那之后我给自己定了规矩:任何「我觉得是因为 X」,在改动之前先拿原文验证 X 是不是真的。 报文、配置、环境变量、实际调用的路径,全部看原文,不看记忆。
这条规矩省下的时间,比任何调试工具都多。
第三步:做一个只做一件事的探针
如果前两步还没找到,就写探针。
探针的原则是剥掉一切业务逻辑,只保留要验证的那一条链路。比如怀疑是网络层的问题,就写一个二十行的脚本,只发一个最小请求、只看有没有回应,不做任何解析、不带任何重试。
然后做对照组:正确的目标发一次、错误的目标发一次、内部的目标发一次。三个结果放在一起,责任边界一下就清楚了——能拿到明确错误的那一段是健康的,「受理了但没有下文」的那一段才是故障所在。
探针要小到能在几十秒内跑完,否则你会舍不得多跑几次对照。
一个附带的教训
复现故障的时候,不要顺手改任何参数。
有一次为了让输出更好看,我在复现时改了一个无关的选项,结果撞上了一条我不知道的路由规则,把原本要验证的那条链路整个换掉了。我对着一个根本不是原故障的现象查了半天。
复现就是复现,要逐字复刻已知出问题那一次的配置。想优化,等查完再说。
写下来之后发现,这三步没有一步是「技术」。画时间线、看原文、做对照,全是很朴素的方法。
难的从来不是方法,是在焦虑的时候还愿意先慢下来做这三件事。


SERVICES.waline即生效。想说话可以先去 留言板,那边我每条都回。