Switch-Router

小谈Nagle

Published at 2022-09-07 | Last Update 2022-09-07

TCP 有大量的参数、开关可以被用户调整. 这一点从 /proc/sys/net/ipv4/tcp_* 以及各类 TCP socket 选项可见一斑.

它让使用者可以千方百计地进行调优, 相比而言, UDP就没什么东西可折腾.

这种灵活性要求使用者必须要明白 TCP 各个参数的作用, 以及网络应用本身的特点, 才能让 TCP 性能达到最佳.

换言之, 没有一个放之四海皆准的 TCP 配置, 对每个网络都适合,要想达到最佳,使用者都必须 case by case 进行调整.

以 Nagle 算法为例. 这一经典算法是为了防止小包(payload长度远小于MSS的TCP报文)在网络中泛滥, 导致网络拥塞设计的.

对于一个TCP连接而言, 启动 Nagle 后, 网络中只能存在一个此连接在该方向的小包. 后续应用下发的小数据, 在已发送小包被 ack 前, 只能在发送端累积而不能立即发送 (除非超时).

然而, Nagle 算法并不适合那种 ping-ping-pong 模式的业务传输.

比如服务端要收到两次 http 请求后, 才回复一次的业务. 这样的业务如果启用 Nagle, 第二个http请求就可能被 Nagle 阻塞, 直到超时才发出.

因此对这种类型的业务, 就需要使用 TCP_NODELAY 禁用 Nagle

int flag = 1;
setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int));
与 Delay-Ack 的纠葛

与 Nagle 类似, Delay-Ack 也是为了减少网络中泛滥的报文而设计的,不同的是,后者的目标是ack报文, 且是生效在接收端,而非发送端.

Nagle 与 Delay-Ack 设计时没有统一的上层,这就使得这两个功能相似的特性是冲突的.

一旦二者同时生效, 就会造成一定程度的死锁: 发送端迟迟等不到ack, 只能超时发送. 接收端迟迟等不到后来的报文, 白白等待一段时间.

结果参考这篇paper

令人讨厌的是, Nagle 与 Delay-Ack 都是默认开启的….

既然已经有了 Delay-Ack, 为什么不能把 Nagle 默认关闭呢…