在某种情况下,我无法收到对特定服务的 HTTP 请求的响应:请求是通过负载平衡器发送的,并且响应太大而无法放入单个数据包中。在这些情况下,永远不会收到响应。两者都不是错误。服务器发送 TCP ACK 以响应包含 HTTP 请求的数据包,但之后什么也没有发生。连接只是挂起等待某些事情发生。无论请求是通过 CURL、节点的 HTTP 库还是 Postman 发送,都会观察到这种行为。
什么原因会导致这种行为?我该如何调试导致问题的原因?
- 我已经验证负载均衡器后面的服务正在接收并响应 HTTP 请求,但是响应在某处丢失了。
- 我们有其他具有类似设置的服务,但不会出现此问题。
- 我们尝试设置负载均衡器的不同实例,但存在同样的问题。
在服务器上的 tshark 中,[TCP Retransmission]
当它通过负载均衡器接收到一个有问题的请求后,我看到了一些条目(当直接接收到相同的请求时不会发生这种情况)。这似乎表明客户端和服务器之间的某些东西正在误导 TCP 流量(高度暗示负载均衡器),但我不明白是什么原因导致这种情况仅当 HTTP 响应被拆分成多个数据包时才会发生,并且仅由此特定服务发生。
导致失败的情况:
直接的 | 负载均衡器 | |
---|---|---|
单包 | ✅ | ✅ |
多个数据包 | ✅ |
答案1
我发现了一个PMTUD 黑洞。
我最初怀疑存在碎片问题,是因为 Cloudflare 发表了一篇关于封包分片。我使用 ping 发送不同大小的数据包确认了该问题:ping -s SIZE
。
首先我尝试了 1500,这是以太网的默认 MTU。
> ping 172.16.5.1 -s 1500
但什么也没发生。它只是挂起,什么也没打印。如果出现问题,您应该会收到如下错误:
ping: sendto: Message too long
Request timeout for icmp_seq 0
最终,我找到了一篇文章更详细地描述了这个问题并推荐了一个修复方法:设置/proc/sys/net/ipv4/tcp_mtu_probing
为1
启用黑洞发现(在 Linux 中默认情况下未启用)