关键在于提升内核全连接队列(ACCEPT队列)容量以应对突发连接,其上限由listen()的backlog参数与net.core.somaxconn的较小值决定;需同步调优半连接队列(tcp_max_syn_backlog)、事件循环accept效率及网卡接收队列(netdev_max_backlog)。

关键在于让内核的全连接队列(ACCEPT队列)能承接住突发的已完成握手连接,避免应用层事件循环来不及调用 accept() 时连接被丢弃。backlog 参数本身不直接控制性能,而是它与内核参数共同决定了这个“缓冲能力”的上限。
理解 listen 的 backlog 实际生效逻辑
应用调用 listen(fd, backlog) 时传入的值,并不会直接成为队列长度。内核会取该值与系统参数 net.core.somaxconn 的较小者作为最终的 ACCEPT 队列最大长度。也就是说,即使你在代码里写了 listen(sockfd, 65535),如果 somaxconn 是默认的 128,那队列实际最多只能存 128 个已建立连接。
- 检查当前限制:
sysctl net.core.somaxconn - 临时调整:
echo 4096 | sudo tee /proc/sys/net/core/somaxconn - 永久生效:在
/etc/sysctl.conf中添加net.core.somaxconn = 4096,然后运行sysctl -p
同步调优半连接队列,防止握手卡在第一步
如果大量客户端同时发起连接,SYN 包会先涌入半连接队列(SYN 队列)。若此队列溢出,客户端根本收不到 SYN+ACK,三次握手连第一步都失败,事件循环再快也无从处理。
- 查看当前值:
sysctl net.ipv4.tcp_max_syn_backlog - 建议设为与
somaxconn同量级或略高,例如8192 - 注意:该参数需配合启用
tcp_syncookies=1作为兜底,防止 SYN Flood 导致服务不可用
确保事件循环自身不成为瓶颈
内核队列只是缓冲区,最终仍需事件循环(如 epoll、kqueue)及时调用 accept() 将连接从队列中取出。如果单次循环只 accept 一个连接,而并发连接数远超此速度,队列很快又会积压。
- 在事件就绪后,应循环调用
accept()直到返回EAGAIN或EWOULDBLOCK,一次性清空当前就绪的连接 - 避免在
accept()后立即进行耗时操作(如数据库连接、复杂鉴权),应尽快交由工作线程或协程处理,保持事件循环轻量 - 监控
ss -s输出中的estab(已建立连接数)与synrecv(半连接数)比例,若estab持续高位且增长缓慢,说明事件循环消费太慢
补充:网络层接收能力也要匹配
当网卡收到海量数据包,它们会先排队进入内核的软中断处理队列。如果这个队列(net.core.netdev_max_backlog)太小,包会在驱动层就被丢弃,连接请求甚至无法抵达 TCP 协议栈。
- 万兆网或高吞吐场景下,建议将该值提升至
16384或更高 - 调整后可通过
cat /proc/net/snmp | grep -i "InErrs\|OutErrs"观察是否有输入错误上升










