40 毫秒延迟是怎么来的
发送方的 Nagle 算法规定「未被确认的小包最多只有一个在路上」,其余小数据先攒着凑成更大的段;接收方的延迟确认策略又不急着回 ACK,倾向于等最多几十毫秒搭别的数据一起发。两者叠加就出现经典的延迟阶梯:应用写完一小段就等对端响应,而这一小段被 Nagle 压住,对端的确认又在等更多数据,双方互相等,延迟凭空多出几十毫秒。
基本信息
| Nagle 算法 | 发送方侧:同一时刻最多一个未被确认的小包在途,其余攒批 |
|---|---|
| 延迟确认 | 接收方侧:不立即回 ACK,等一小段时间搭车发送,Linux 上由启发式策略决定 |
| 组合问题 | 写、写、读模式下第二段小数据被压住,同时等对方的延迟确认,形成卡顿 |
| 常用开关 | TCP_NODELAY 置 1 即关闭 Nagle,对端无法控制本端行为 |
| 适用场景 | 交易、游戏、RPC 等低延迟链路,批量吞吐场景则适合保留 Nagle |
关闭 Nagle 的常见写法
关闭 Nagle 的常见写法
import socket
s = socket.create_connection(("api.example.com", 80))
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # 应用层关掉 Nagle,小包立即发出
s.sendall(b'{"a":1}')
# nginx 侧对应指令:tcp_nodelay on; # 新版本默认即为 on,作用于客户端与上游连接
# 后端的 Java 服务通常在连接池上配置 TCP_NODELAY,或用 Netty 的 TCP_NODELAY 选项现象与处置对照
| 现象 | 原因 | 处置 |
|---|---|---|
| 请求响应都是小包,耗时呈固定阶梯 | Nagle 与延迟确认叠加 | 应用侧打开 TCP_NODELAY |
| 只在传大文件时吞吐上不去 | 窗口、拥塞控制或链路限制 | 不要盲目关 Nagle,先看重传与窗口 |
| 关闭后包量明显上升、带宽利用率反而降 | 小包变多,头部开销占比升高 | 评估按包量计费的链路成本 |
| 延迟忽高忽低、无明显阶梯 | 更像网络抖动或排队 | 用抓包看时间差,别急着改套接字选项 |
延迟确认并不是固定 40 毫秒:Linux 用的是快速确认与延迟确认并存的启发式策略,实际等待时长取决于流量形态与内核版本,40 毫秒只是最常见量级下的典型上限。
关闭 Nagle 之后小包数量上升,在按包量或连接数计费的云环境里成本会变化,调优时应把延迟收益与账单、限速一起评估。