客户端和服务端节点都是 CentOS7.9/X86_64,如果直接向服务端发送 HTTP POST 请求,大概有 0.2% 的情况会超时,如果在客户端节点通过 NGINX 代理发送 HTTP POST 请求,大概有 20% 的情况会超时,我确认只有一个后端节点有这个问题,其他节点都是 100% 成功,即使吞吐量更高。
经过对后端节点的tcpdump以及Wireshark分析,发现请求成功,可以正常收到tcp包,如下图所示:
也就是说,TCP 接收方会向每个较大的 TCP 负载发送 ACK。
对于失败的请求,tcp 接收器仅确认每个 tcp 数据包的 1398 大小。1398 是 MSS 减去 TCP/IP 标头后的最小 tcp 有效负载。(1410 - 66 = 1398),如下所示:
TCP 发送方在 60 秒内发送了 8 次 TCP 重传,但 TCP 接收方从未再次发送确认。HTTP 服务器在 60 秒的读取超时时间内关闭了连接。
由于数据包是在服务器端节点捕获的,因此数据包似乎是在内核 TCP 堆栈中丢失的,而不是在网络中丢失的。而在客户端节点上,使用 tcpdump 观察到客户端快速收到了来自服务器的每个确认。
有人可以帮忙吗?提前致谢。
答案1
根本原因如下:
客户端(nginx)没有发送PSH标志。
服务器端为了接收更多字节而扩大TCP窗口大小并且不发回ACK。
客户端发送更多字节。
服务器接收更多字节并扩大窗口大小。但没有发送ACK标志。
100ms后,客户端认为数据包丢失,并重新发送数据包。
服务器发送“重复 ACK”并继续扩大窗口大小,因为尽管字节重复,但它收到了更多的字节。
服务器发送“重复 ACK”并继续扩大窗口大小,因为尽管有重复的字节,但它收到了更多的字节。两者都饿死了。
解决方案:
nginx 即使打开配置也不会发送 PSH。
在服务器端进行配置,将最大 TCP 窗口大小减少到 32k,解决了该问题。