出现 timeout 问题时如何正确甩锅
问题背景
作为开发,我猜你一定遇到过下面这样扯皮的经历,现在没遇到没关系,你迟早会遇到。
我来描述下。
假设,我们有个服务调用链路是 A → B .
现在,A 服务接口调用 B 服务的 API 出现 io timeout 问题。
A 设置的 timeout 是 5s。也就是说从 A 的视角看来,它发出了一个请求到 B,苦等 5s 都没有任何响应。
这时候,如果你是 A,你大概率心里会骂 B 服务的开发是小可爱。
但就算你是素质教育漏网之鱼,看在每个月 30 号都能准时到账工资的面子上。
你都得客客气气地对 B 服务的开发说:“同学,你的服务好像超时了,辛苦帮忙看下哦~”。
但诡异的是,B 服务的开发看了下监控和日志。发现 api latency 却小于 100ms. 且单独使用 curl 命令调用 B 的 API 也一切正常。
然后还将 curl 命令扔给了你,回复说:"是不是你的调用传参错了呀,你可以参考下这个 curl 命令对照下,它是好的哦~"。
这就有意思了。
上游(A 服务)认定下游有问题,毕竟 io timeout 了。但下游(B 服务)下游会认为自己没问题,毕竟 curl 都没问题,可能是数据发到了上游但上游逻辑有问题导致处理超时,各执一词。都在等对方进一步排查自己的代码。
好一个你不动我不动。
这,不就“死锁”了吗?
这么破?
如果你没看今天的文章,最终结果,大概率就是大家将锅甩来甩去,最终一致决定将锅甩给“网络波动”。
今天这篇文章就用这个案例来告诉大家,怎么样才能定位这类问题出在哪一方,让大家在甩锅的时候更加有理有据,得心应手。
排查方案
A 出现 timeout,但 B 的监控和日志 latency 都小于 100ms。那说明对 B 服务来说,它自己已经返回了,那它到底返没返回到 A 呢?
此时可以在 A 服务那进行抓包,在内核的网络接口层判断数据是否已经送到 A 对应实例中。
如果已经到 A 服务所在的实例了,那多半就是 A 服务自己收到数据后处理超时了。
如果数据压根没到 A 服务所在实例的网络接口层,那必然就是 B 服务自己的问题了。
执行以下步骤方法:
1.抓包
在 A 机器执行 tcpdump 进行抓包
| tcpdump -i any -nn -tttt -s0 -v host bbb.com -w test.pcap | | ------------------------------------------------------------- |
下面是对每个选项的解释:
tcpdump: 这是一个用于抓取网络数据包的命令行工具。-i any: 指定抓取数据包的网络接口。在这里,any表示抓取所有可用的网络接口。-nn: 禁用名称解析,这样输出中会显示 IP 地址而不是主机名。-tttt: 显示时间戳以及日期。在这里,-tttt会显示精确到微秒级别的时间戳。-s0: 指定抓取数据包的最大长度。在这里,-s0表示抓取完整的数据包。-v: 输出详细的信息,包括一些解析后的字段。host bbb.com: 指定抓取的目标主机为bbb.com,也就是 B 服务 的域名。-w test.pcap: 将捕获的数据包保存到名为test.pcap的文件中。
总的来说,这个命令会抓取所有网卡上的数据包,显示 IP 地址和精确到微秒级别的时间戳,抓取完整的数据包,输出详细信息,并将符合目标主机bbb.com的数据包保存到名为test.pcap的文件中。
2.复现
此时执行复现问题的操作。预期抓包界面会从 got 0 变成 got x (x 是正整数),意思是抓到了 x 个数据包。
如果有必要,还可以执行 curl 命令, 抓取一些正常调用成功的包,方便后续对比正常包和异常包之间的差异。
3.导出文件
复现问题后,通过 ctrl c 停止抓包。当前文件夹下会多一个 test.pcap 文件,用 scp 命令将它传到安装了 wireshark 的机器里。
4.分析数据包
在 mac 本地通过 wireshark 打开test.pcap文件。
找到报错时的 http 请求,可以通过输入 http 去筛选。
找到目标请求,通过右键 →follow→tcp stream 跟踪某个 tcp 连接从建立到断开的流程。
就能看到以下画面。
注意上面,端口号是 20162 的是客户端,也就是 A. 端口号是 80 的是服务端,也就是 B。
从数据包流向可以看出,A 在发出 http 请求后,B 数据包只回了个 ack 包,一直没有回复对应的 http 状态码 200 的数据包。直到 5s 超时,客户端一直等不到服务端的 http 响应,于是就主动触发 tcp 四次挥手断开连接。
所以从这里就可以看出。问题确实是出在服务端,也就是 B 节点。而不是 A 节点收到数据包后没处理直到超时。
原因
但问题就来了,为什么执行 curl 时就是好的呢?
我们可以对比下 curl 命令时的数据包跟异常情况下数据包的差异。
正常情况下使用 curl 的数据包应该是长下面这样。会有一个 http 状态码 200 的响应包。
依次对比域名、入参 payload 和 header 等信息,就发现了 header 头有一些差异。
正常的 http 的 header 头长这样。
报错的 http 请求的 header 头长这样。
将错误的 header 头陆续加入到 curl 命令中构造调用,最终发现只要存在 X-Request-Id 头,就会出现 timeout 问题。
到这里问题就清晰多了,将问题反馈给 B 节点对应开发,进一步排查就好了。
最终,问题的根因是 B 服务内部存在逻辑问题。
之所以 A 服务出现 timeout,B 服务的监控和日志却一切正常,单纯只是因为监控和日志正好没有覆盖报错问题区域。
这口锅,请 B 服务的老哥稳稳接住!
最后
这篇文章通过一个常见的 timeout 例子,讲解了 wireshark 初级使用方法。
简单但是超实用。
建议大家掌握下这个技能,你未来甩锅的时候一定用得上!!
#计算机基础# #后端#