tcp40ms延迟问题分析

背景

tcp抓包发现服务端回复ack存在40ms左右延迟

dmqEqK.png

问题排查过程

通过查阅资料发现tcp40ms延迟不是一个偶然的现象。先给出结论:客户端开启nagle算法+服务端延迟ack,延迟ack的超时时间为40ms。

先介绍下重要的概念

delay ack

在TCP建立连接之后,最开始的数据交互是处于quick ack mode,顾名思义就是当对端收到数据立马就会回复ack。在接下来的数据交互过程中,服务端仍然执行快速ack,服务端然后往对端发送交互数据,此时系统探测到了这样一种交互行为,于是开启ping pong mode。服务端开启了ping pong mode之后,收到数据不会立马回复对端ack,而是在回复响应数据时候带上ack,这样减少了网络中的包量。但是如果服务端在延时ack时间都没有响应客户端的请求就可能出现短暂的响应延迟。

dmqAr6.png

nagle算法

Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段。 所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。

Nagle算法的规则(tcp_output.c文件里tcp_nagle_check函数注释):

(1)如果包长度达到MSS,则允许发送;

(2)如果该包含有FIN,则允许发送;

(3)设置了TCP_NODELAY选项,则允许发送;

(4)未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;

(5)上述条件都未满足,但发生了超时(一般为200ms),则立即发送。

有了上面的知识铺垫,我们具体分析下客户端开启nagle,服务端处于delay ack 状态下,客户端不同大小数据请求下延时情况。

  1. 当客户端请求数据小于mss大小

假设客户端之前的数据已经全部被ack了,此时发送一个新的数据请求,小于mss,对端服务器在收到数据之后,不会立马回复ack,但是在delay ack超时之前能够回复响应数据,顺带就把ack发过去了,所以不会出现我们说的40ms延迟。

  1. 客户端请求数据大于mss

由于请求数据大于mss,一个包装不下这么多数据,tcp会将数据分成小包发送,在发送第一个小包之后,服务端延迟ack,同时由于数据不全,服务端不能立即响应,于是就在等待延迟ack超时,同时客户端这边开启了nagle,之前发送的数据包还没有被ack,待发送的数据包小于mss,于是也进入等待,这样一来双方都处于等待,进入了短暂的”死锁“。

解决方案

有了上面的分析,我们可以从两个方向来考虑如何解决40ms延迟。

  1. 服务端关闭延迟ack ;echo 1 > /proc/sys/net/ipv4/tcp_no_delay_ack

  2. 客户端关闭nagle;通过设置TCP_NODELAY来实现