我有一个从 Solaris 迁移到 RH Linux 的客户端/服务器应用程序 (Java)。自从我开始在 RH 中运行它以来,我注意到一些与延迟有关的问题。我设法隔离了如下所示的问题:
- 客户端连续向服务器发送 5 条消息(每条 32 字节)(相同的应用程序时间戳)。
- 服务器回显消息。
- 客户端接收回复并打印每个味精的往返时间。
在 Solaris 中,一切都很好:我同时收到所有 5 个回复,大约在发送原始消息后 80 毫秒(客户端和服务器相距数千英里:我的 ping RTT 为 80 毫秒,一切正常)。
在 RH 中,前 3 条消息正常回显(它们在发送后 80 毫秒到达),但接下来的 2 条消息在 80 毫秒后到达(因此总共 160 毫秒 RTT)。
模式总是一样的。显然看起来像一个 TCP 问题。
在我的 solaris 机器上,我之前使用 2 个特定选项配置了 tcp 堆栈:
- 全局禁用nagle算法
- 将 tcp_deferred_acks_max 设置为 0
在 RH 上,无法全局禁用 nagle,但我在所有应用程序的套接字 (TCP_NODELAY) 上禁用了它。
所以我开始玩 tcpdump (在服务器机器上),并比较了两个输出:
太阳能:
22 2.085645 client server TCP 56150 > 6006 [PSH, ACK] Seq=111 Ack=106 Win=66672 Len=22 "MSG_1 RCV"
23 2.085680 server client TCP 6006 > 56150 [ACK] Seq=106 Ack=133 Win=50400 Len=0
24 2.085908 client server TCP 56150 > 6006 [PSH, ACK] Seq=133 Ack=106 Win=66672 Len=22 "MSG_2 RCV"
25 2.085925 server client TCP 6006 > 56150 [ACK] Seq=106 Ack=155 Win=50400 Len=0
26 2.086175 client server TCP 56150 > 6006 [PSH, ACK] Seq=155 Ack=106 Win=66672 Len=22 "MSG_3 RCV"
27 2.086192 server client TCP 6006 > 56150 [ACK] Seq=106 Ack=177 Win=50400 Len=0
28 2.086243 server client TCP 6006 > 56150 [PSH, ACK] Seq=106 Ack=177 Win=50400 Len=21 "MSG_1 ECHO"
29 2.086440 client server TCP 56150 > 6006 [PSH, ACK] Seq=177 Ack=106 Win=66672 Len=22 "MSG_4 RCV"
30 2.086454 server client TCP 6006 > 56150 [ACK] Seq=127 Ack=199 Win=50400 Len=0
31 2.086659 server client TCP 6006 > 56150 [PSH, ACK] Seq=127 Ack=199 Win=50400 Len=21 "MSG_2 ECHO"
32 2.086708 client server TCP 56150 > 6006 [PSH, ACK] Seq=199 Ack=106 Win=66672 Len=22 "MSG_5 RCV"
33 2.086721 server client TCP 6006 > 56150 [ACK] Seq=148 Ack=221 Win=50400 Len=0
34 2.086947 server client TCP 6006 > 56150 [PSH, ACK] Seq=148 Ack=221 Win=50400 Len=21 "MSG_3 ECHO"
35 2.087196 server client TCP 6006 > 56150 [PSH, ACK] Seq=169 Ack=221 Win=50400 Len=21 "MSG_4 ECHO"
36 2.087500 server client TCP 6006 > 56150 [PSH, ACK] Seq=190 Ack=221 Win=50400 Len=21 "MSG_5 ECHO"
37 2.165390 client server TCP 56150 > 6006 [ACK] Seq=221 Ack=148 Win=66632 Len=0
38 2.166314 client server TCP 56150 > 6006 [ACK] Seq=221 Ack=190 Win=66588 Len=0
39 2.364135 client server TCP 56150 > 6006 [ACK] Seq=221 Ack=211 Win=66568 Len=0
红帽:
17 2.081163 client server TCP 55879 > 6006 [PSH, ACK] Seq=111 Ack=106 Win=66672 Len=22 "MSG_1 RCV"
18 2.081178 server client TCP 6006 > 55879 [ACK] Seq=106 Ack=133 Win=5888 Len=0
19 2.081297 server client TCP 6006 > 55879 [PSH, ACK] Seq=106 Ack=133 Win=5888 Len=21 "MSG_1 ECHO"
20 2.081711 client server TCP 55879 > 6006 [PSH, ACK] Seq=133 Ack=106 Win=66672 Len=22 "MSG_2 RCV"
21 2.081761 client server TCP 55879 > 6006 [PSH, ACK] Seq=155 Ack=106 Win=66672 Len=22 "MSG_3 RCV"
22 2.081846 server client TCP 6006 > 55879 [PSH, ACK] Seq=127 Ack=177 Win=5888 Len=21 "MSG_2 ECHO"
23 2.081995 server client TCP 6006 > 55879 [PSH, ACK] Seq=148 Ack=177 Win=5888 Len=21 "MSG_3 ECHO"
24 2.082011 client server TCP 55879 > 6006 [PSH, ACK] Seq=177 Ack=106 Win=66672 Len=22 "MSG_4 RCV"
25 2.082362 client server TCP 55879 > 6006 [PSH, ACK] Seq=199 Ack=106 Win=66672 Len=22 "MSG_5 RCV"
26 2.082377 server client TCP 6006 > 55879 [ACK] Seq=169 Ack=221 Win=5888 Len=0
27 2.171003 client server TCP 55879 > 6006 [ACK] Seq=221 Ack=148 Win=66632 Len=0
28 2.171019 server client TCP 6006 > 55879 [PSH, ACK] Seq=169 Ack=221 Win=5888 Len=42 "MSG_4 ECHO + MSG_5 ECHO"
29 2.257498 client server TCP 55879 > 6006 [ACK] Seq=221 Ack=211 Win=66568 Len=0
所以,我得到确认 RH 的事情没有正常工作:数据包 28 发送得太晚了,看起来服务器在做任何事情之前正在等待数据包 27 的 ACK。
在我看来这是最可能的原因...
然后我意识到 Solaris 和 RH 转储上的“Win”参数不同:Solaris 上为 50400,RH 上只有 5888。这是另一个提示...
我阅读了有关幻灯片窗口和缓冲区窗口的文档,并在我的套接字上使用了 java 中的 rcvBuffer 和 sendBuffer,但从未设法将这个 5888 值更改为其他任何值(我每次都直接使用 tcpdump 进行检查)。
有人知道怎么做这个吗 ?我很难获得明确的信息,因为在某些情况下,我可能需要绕过“自动协商”,等等......
通过在 RH 上将“tcp_slow_start_after_idle”参数设置为 0,我最终设法仅部分摆脱了最初的问题,但它根本没有改变“win”参数。前 4 组 5 条消息也存在同样的问题,在 tcpdump 中使用 TCP 重新传输和 TCP Dup ACK,然后对于接下来的所有 5 条消息组,问题完全消失了。
对我来说,这似乎不是一个非常干净和/或通用的解决方案。我真的很想在两个操作系统下重现完全相同的条件。
我会继续研究,但非常感谢 TCP 专家的任何帮助!