温馨提示×

为何Linux backlog溢出

小樊
72
2026-07-18 07:38:47
栏目: 智能运维

Linux 中 backlog 溢出 通常指的是 TCP 监听队列(listen backlog)溢出,常见于高并发服务(如 Nginx、Redis、MySQL、自研服务)。下面从 原理、原因、现象、排查与解决 几个层面说明。


一、什么是 backlog

在 Linux 网络编程中,socket 建立过程如下:

  1. 服务端调用 socket()
  2. 调用 bind()
  3. 调用 listen(int sockfd, int backlog)
  4. 客户端发起 connect()
  5. 三次握手完成
  6. 服务端调用 accept() 取连接

backlog 表示:

已完成三次握手、但还未被 accept() 取走的 ESTABLISHED 连接队列长度上限


二、Linux 中 backlog 的真实含义(重点)

在 Linux 中,listen() 的 backlog 不是单一队列长度,而是:

实际队列长度 = min(backlog, net.core.somaxconn)

并且 Linux 内部有两个队列(历史实现略有差异):

队列 说明
半连接队列(SYN queue) 收到 SYN,未完成握手
全连接队列(Accept queue) 完成三次握手,等待 accept

backlog 溢出通常指:全连接队列已满


三、backlog 溢出的常见原因

1️⃣ backlog 设置过小(最常见)

listen(fd, 128);

而系统默认:

net.core.somaxconn = 128

高并发下瞬间被打满。


2️⃣ 应用 accept 不及时

  • accept() 调用慢
  • 单线程 / 阻塞模型
  • 业务逻辑重
  • 锁竞争、GC(Java)

即使 backlog 很大,也会被迅速填满。


3️⃣ 突发连接风暴

  • 秒杀、重启、重试
  • 客户端大量短连接
  • 健康检查(如 LB)

4️⃣ SYN Flood 攻击

恶意大量 SYN,导致:

  • 半连接队列满
  • 系统丢弃或回 SYN+ACK
  • 间接影响全连接队列

5️⃣ 系统参数限制

以下参数过小也会导致问题:

net.core.somaxconn
net.ipv4.tcp_max_syn_backlog

四、backlog 溢出的现象

1️⃣ 客户端异常

  • connection refused
  • connection timeout
  • reset by peer

2️⃣ 服务端现象

  • 连接数上不去
  • CPU 不高但吞吐上不去
  • accept() 阻塞

五、如何确认是 backlog 溢出

✅ 1. 查看全连接队列溢出(最直接)

ss -lnt

示例:

Recv-Q Send-Q Local Address:Port
128    128    0.0.0.0:80

如果 Recv-Q == Send-Q,说明 队列已满


✅ 2. 查看系统丢包统计

netstat -s | grep -i listen

或:

grep -i overflow /proc/net/snmp

看到类似:

ListenOverflows: 12345
ListenDrops: 12345

✅ 明确说明 backlog 溢出


✅ 3. 抓包观察

tcpdump -i eth0 port 80

可能看到:

  • 客户端重传 SYN
  • 服务端不回 SYN+ACK

六、如何解决 backlog 溢出

✅ 1. 增大 backlog(必须)

应用层:

listen(fd, 1024);

系统层:

sysctl -w net.core.somaxconn=1024
sysctl -w net.ipv4.tcp_max_syn_backlog=2048

写入配置文件:

/etc/sysctl.conf

✅ 2. 提高 accept 处理能力

  • 多线程 / 多进程
  • 使用 epoll
  • 非阻塞 IO
  • 减少 accept 后的阻塞操作

✅ 3. 使用连接池 / 长连接

避免大量短连接冲击 backlog。


✅ 4. 开启 SYN Cookie(防攻击)

sysctl -w net.ipv4.tcp_syncookies=1

✅ 5. 限流 & 重试策略

  • 客户端增加退避重试
  • 接入层(LB)限流
  • 使用队列削峰

七、常见误区

backlog = 最大连接数
✅ 只是 已完成握手但未 accept 的队列

调大 backlog 就一定能抗并发
✅ 还取决于 accept() 速度和系统资源

ESTABLISHED 很多就是 backlog 不够
✅ 要看 Recv-Q 是否等于 Send-Q


八、一句话总结

Linux backlog 溢出本质原因:已完成 TCP 三次握手的连接,来不及被应用 accept,导致队列被打满,新连接被丢弃。

如果你愿意,可以告诉我:

  • 哪个服务(Nginx / Java / C++)
  • 短连接还是长连接
  • 并发量级

我可以给你一个 针对性的参数和架构优化方案

0