在某些情况下,我无法从 HTTP 请求接收到对特定服务的响应:请求是通过负载平衡器发送的,并且响应太大而无法放入单个数据包中。在这些条件下,永远不会收到响应。两者都不是错误。服务器发送一个 TCP ACK 来响应包含 HTTP 请求的数据包,但之后什么都没有。连接只是挂起等待发生的事情。无论请求是通过 CURL、节点的 HTTP 库还是 Postman 发送的,都会观察到该行为。
什么可能导致这种行为?如何调试导致问题的原因?
- 我已经验证负载均衡器后面的服务正在接收并响应 HTTP 请求,但是响应在某处丢失了。
- 我们还有其他类似设置的服务不会出现此问题。
- 我们尝试设置负载均衡器的不同实例,但它有同样的问题。
在服务器上的 tshark 中,[TCP Retransmission]
在它通过负载均衡器接收到一个有问题的请求后,我看到了一些条目(当直接接收到相同的请求时不会发生这种情况)。这似乎表明客户端和服务器之间的某些东西误导了 TCP 流量(高度暗示负载平衡器),但我不明白只有当 HTTP 响应被拆分为多个数据包时才会导致这种情况发生,并且只有这个特定的服务。
导致失败的条件:
直接的 | 负载均衡器 | |
---|---|---|
单包 | ✅ | ✅ |
多个数据包 | ✅ | ? |
网络拓扑结构:
Client -> Load Balancer -> Service
原来我发现了一个PMTUD 黑洞。
由于 Cloudflare 的一篇关于数据包碎片的文章,我首先怀疑存在碎片问题。我确认了使用 ping 发送不同大小的数据包的问题:
ping -s SIZE
.首先,我尝试了 1500,这是以太网的默认 MTU。
但是什么也没发生。它只是挂起并没有打印任何内容。如果出现问题,您应该会收到如下错误:
最终,我找到了一篇更详细地描述了这个问题的文章,并推荐了一个修复方法:设置
/proc/sys/net/ipv4/tcp_mtu_probing
为1
启用黑洞发现(Linux 中默认情况下不启用此功能?♂️)。我实际上无法修改该文件,因此我按照建议进行了设置,/etc/sysctl.conf
然后通过sysctl --system
. 毕竟,我能够收到来自服务器的响应。MTU 探测增加了响应的开销(明显延迟),所以我仍然想弄清楚为什么没有收到 ICMP 数据包,但至少我现在理解了这个问题并有了一个可行的解决方案。
更新:原来 LAG 的 MTU 为 1500,因此它正在丢弃数据包。