linux socket backlog 控制accept队列长度,取listen()参数与net.core.somaxconn较小值;过小导致已完成握手连接被丢弃,引发connection refused;需协同调优应用、系统参数及内存资源,并通过ss、/proc/net/snmp等验证实效。

Linux socket 的 backlog 大小直接影响服务器在高并发场景下的连接接纳能力。它不是“能同时处理多少连接”,而是“能暂存多少已完成三次握手、但尚未被应用调用 accept() 取走的连接”。这个队列一旦溢出,新连接就会被内核直接拒绝(返回 ECONNREFUSED),用户看到的就是“连接被拒”,哪怕服务器 CPU 和内存还很空闲。
backlog 实际控制的是 accept 队列长度
从 Linux 2.2 起,内核维护两个独立队列:
-
SYN 队列(半连接队列):存放收到 SYN、尚未完成三次握手的连接,大小由
net.ipv4.tcp_max_syn_backlog控制; -
Accept 队列(全连接队列):存放已完成三次握手、等待应用
accept()的连接,其实际长度取listen(sockfd, backlog)中传入的值与net.core.somaxconn二者中的较小者。
也就是说,即使你在代码里写了 listen(fd, 65535),如果 net.core.somaxconn = 128(很多发行版默认值),那真正生效的仍是 128。这是最常被忽略的瓶颈点。
过小的 backlog 会直接丢连接
当并发连接请求速率 > 应用 accept 处理速率时,accept 队列迅速填满。此时:
- 后续到达的已完成三次握手的连接会被内核静默丢弃;
- 客户端收不到 SYN-ACK,或收到 RST,表现为连接超时或 Connection refused;
- 现象可通过
netstat -s | grep -i "listen"查看:1234 times the listen queue of a socket overflowed或1234 SYNs to LISTEN sockets dropped。
合理设置 backlog 需兼顾三方面
不能盲目调大,也不能维持默认值:
-
应用层:检查服务代码中
listen()的第二个参数(如 Nginx 的listen 80 backlog=4096;,Go 默认用syscall.SOMAXCONN,Java Netty 默认 128); -
系统层:确保
net.core.somaxconn≥ 应用设置值(推荐 4096–65535),并同步调整net.ipv4.tcp_max_syn_backlog(建议 ≥ somaxconn); - 资源约束:每个待 accept 连接占用约 1–2 KB 内核内存,万级 backlog 增加几十 MB 内存开销,需确认物理内存充足。
验证和持续观察很关键
改完参数后不能只看是否“不报错”,还要确认是否真起效:
- 运行
ss -ltn,看Recv-Q列是否长期接近你设的 backlog 值; - 压测时监控
/proc/net/snmp中TcpExt: ListenOverflows和ListenDrops计数是否增长; - 搭配
perf trace -e 'syscalls:sys_enter_accept*'观察 accept 调用频次与延迟,判断是队列堵了还是应用处理慢了。











