首页/系统/Linux 内核与性能调优速查/TCP_DEFER_ACCEPT 减少空连接唤醒
网络栈调优98 条

TCP_DEFER_ACCEPT 减少空连接唤醒

让内核在收到首个数据包后再唤醒 accept, 减少对空连接轮询与唤醒开销, 适合先连接后发数据的服务。

什么是 TCP_DEFER_ACCEPT

默认情况下,三次握手一完成内核就把连接放入 accept 队列并唤醒应用,应用随后可能发现对端其实什么都没发过来。TCP_DEFER_ACCEPT 让内核等到连接上收到第一个数据包(或等待超时)之后才唤醒监听方,从而减少空连接带来的唤醒与系统调用开销。它改变的是唤醒时机,不改变握手本身,因此适合“连上就发数据”的协议(HTTP、Redis 等),不适合连接后长时间静默的协议。

基本信息

接口setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &secs, sizeof(secs))
参数含义等待数据的最大秒数,超时后仍会唤醒
nginx 对应listen 80 deferred;
适用协议连接建立后立即发送请求的短连接场景
不适用连接后由服务端先发数据的协议
观测ss -ltn 的 Recv-Q、accept 队列长度

典型使用场景

  • 高并发短连接服务:降低空连接带来的唤醒风暴
  • 压测与抗扫描:大量只握手不发数据的连接不再逐个唤醒应用
  • HTTP/HTTPS 前端:请求总是由客户端先发,符合该语义
  • 降低系统调用次数:把部分 CPU 开销从应用侧移到内核判定
  • 与负载均衡配合:确认 L4 探活不会因未发数据而受影响

用法示例

用法示例
int secs = 3;
/* 等待至多 3 秒;期间没有数据则不唤醒 accept */
setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &secs, sizeof(secs));
/* 设 0 表示关闭该行为,恢复默认语义 */
int off = 0;
setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &off, sizeof(off));

nginx 与观察

nginx 与观察
server {
    listen 80 deferred;   # 配合内核行为,握手后等数据
    listen 443 ssl deferred;
}
ss -ltn | head -5       # 看监听队列与 Recv-Q
top -b -n1 | head -5    # 对比开启前后的软中断与应用唤醒开销

行为对照

配置唤醒时机适用判断
默认握手完成即唤醒服务端需先发数据的协议
deferred(secs>0)收到首个数据或超时HTTP、Redis 这类客户端先发的协议
deferred 秒数过大超时前一直不处理探活可能被拖延,需要评估
对静默协议启用连接迟迟不被处理属于误用,会表现为请求超时
它只作用于监听 socket 的唤醒语义:连接在 accept 队列中的状态与超时参数仍由内核维护,排查“连接建立了但应用没处理”时要先确认队列与超时设置。
对连接后长时间不发数据的协议强行启用,会导致连接迟迟不被处理,最终被对端或中间设备超时断开,表现为难以解释的握手成功但无响应。

易错点

  • 对连接后长时间不发数据的协议强行启用,会导致连接迟迟不被处理,业务表现为超时无响应
  • 忽略它只影响监听 socket 的唤醒语义,把它当成提高并发能力的通用开关,实际收益取决于流量形态
  • 把 defer 超时秒数设得过大,探活与慢客户端都被拖住,失败反馈明显滞后
  • 开启后不对比 accept 队列与应用唤醒指标,无法判断优化是否真正生效

数据来源与参考

  • man 7 tcp
  • nginx 官方文档 listen 指令
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌