为什么加了 TCP_NODELAY ,时延就从 39.2ms 降低到 2.8ms?
为什么本地测试的平均时延是 55ms,而不是 ping 的时延 26ms?
TCP 协议究竟是怎么发送数据包的?
TCP_NODELAY 何许人也?
在 Socket 编程中,TCP_NODELAY 选项是用来控制是否开启 Nagle 算法。
在 Java 中,为 ture 表示关闭 Nagle 算法,为 false 表示打开 Nagle 算法。你一定会问 Nagle 算法是什么?
Nagle 算法是什么鬼?
Nagle 算法是一种通过减少通过网络发送的数据包数量来提高 TCP/IP 网络效率的方法。
它使用发明人 John Nagle 的名字来命名的,John Nagle 在 1984 年首次用这个算法来尝试解决福特汽车公司的网络拥塞问题。
试想如果应用程序每次产生 1 个字节的数据,然后这 1 个字节数据又以网络数据包的形式发送到远端服务器,那么就很容易导致网络由于太多的数据包而过载。在这种典型情况下,传送一个只拥有1个字节有效数据的数据包,却要花费 40 个字节长包头(即 IP 头部 20 字节 + TCP 头部 20 字节)的额外开销,这种有效载荷(payload)的利用率是极其低下。
具体的做法就是:
如果发送内容大于等于 1 个 MSS, 立即发送;
如果之前没有包未被 ACK, 立即发送;
如果之前有包未被 ACK, 缓存发送内容;
如果收到 ACK, 立即发送缓存的内容。(MSS 为 TCP 数据包每次能够传输的最大数据分段)
Delayed ACK 又是什么玩意?
大家都知道 TCP 协议为了保证传输的可靠性,规定在接受到数据包时需要向对方发送一个确认。
只是单纯的发送一个确认,代价会比较高(IP 头部 20 字节 + TCP 头部 20 字节)。
TCP Delayed ACK(延迟确认)就是为了努力改善网络性能,来解决这个问题的,它将几个 ACK 响应组合合在一起成为单个响应,或者将 ACK 响应与响应数据一起发送给对方,从而减少协议开销。
具体的做法是:
当有响应数据要发送时,ACK 会随响应数据立即发送给对方;
如果没有响应数据,ACK 将会延迟发送,以等待看是否有响应数据可以一起发送。在 Linux 系统中,默认这个延迟时间是
40ms;
如果在等待发送 ACK 期间,对方的第二个数据包又到达了,这时要立即发送 ACK。但是如果对方的三个数据包相继到达,第三个数据段到达时是否立即发送 ACK,则取决于以上两条。
Nagle 与 Delayed ACK 一起会发生什么化学反应?
Nagle 与 Delayed ACK 都能提高网络传输的效率,但在一起会好心办坏事。
例如,以下这个场景:
A 和 B 进行数据传输 : A 运行 Nagle 算法,B 运行 Delayed ACK 算法。
如果 A 向 B 发一个数据包,B 由于 Delayed ACK 不会立即响应。而
A 使用 Nagle 算法,A 就会一直等 B 的 ACK,ACK 不来一直不发送第二个数据包,如果这两个数据包是应对同一个请求,那这个请求就会被耽误了 40ms。
我们来抓个包验证下,在后端HTTP服务上执行以下脚本,就可以轻松完成抓包过程。
tcpdump -i eth0 tcp and host 10.48.159.165 -s 0 -w traffic.pcap
如下图,这是使用 Wireshark 分析包内容的展示,红框内是一个完整的 POST 请求处理过程,看 130 序号和 149 序号之间相差 40ms(0.1859 - 0.1448 = 0.0411s = 41ms),这个就是 Nagle 与 Delayed ACK 一起发送的化学反应,其中 10.48.159.165 运行的是 Delayed ACK,10.22.29.180 运行的是 Nagle 算法。
10.22.29.180 在等 ACK,而 10.48.159.165 触发了 Delayed ACK,这样傻傻等了 40ms。
这也就解释了为什么测试环境耗时是 39.2ms,因为大部分都被 Delayed ACK 的 40ms 给耽误了。
但是本地复现时,为什么本地测试的平均时延是 55ms,而不是 ping 的时延 26ms?
如下图,红框内是一个完整的 POST 请求处理过程,看 8 序号和 9 序号之间相差 25ms 左右,再减去网络延时约是ping延时的一半 13ms,因此 Delayed Ack 约 12ms 左右(由于本地是 MAC 系统与 Linux 有些差异)。
image.pngLinux 使用的是 /proc/sys/net/ipv4/tcp_delack_min 这个系统配置来控制 Delayed ACK 的时间,Linux 默认是 40ms。
为什么 TCP_NODELAY 能够解决问题?
TCP_NODELAY 关闭了 Nagle 算法,即使上个数据包的 ACK 没有到达,也会发送下个数据包,进而打破 Delayed ACK 造成的影响。
一般在网络编程中,强烈建议开启 TCPNODELAY,来提升响应速度。
当然也可以通过 Delayed ACK 相关系统的配置来解决问题,但由于需要修改机器配置,很不方便,因此,这种方式不太推荐。
网友评论