线上服务突然大量Connection refused,原来是这个队列满了
上周线上告警,服务间调用大量超时,日志里全是Connection refused。排查发现是TCP全连接队列满了,很多人不知道这个坑。
先搞懂TCP连接的两个队列
客户端发起连接时,服务端有两个队列在工作:
客户端 服务端
| |
|-------- SYN ---------> |
| [半连接队列:存放SYN_RECV状态的连接]
|<------ SYN+ACK -------| |
| |
|-------- ACK ---------> |
| [全连接队列:存放ESTABLISHED但未被accept的连接]
| |
- 半连接队列(SYN Queue):收到SYN后,等待第三次握手
- 全连接队列(Accept Queue):三次握手完成,等待应用程序accept
任何一个队列满了,新连接都会被拒绝。
实战:怎么知道队列满了
1. 查看全连接队列溢出
# 查看溢出统计(数值持续增长说明有问题)
netstat -s | grep -i "listen"
# 输出示例:
# 12345 times the listen queue of a socket overflowed
# 12345 SYNs to LISTEN sockets dropped
或者用ss命令:
# 查看每个监听端口的队列情况
ss -lnt
# 输出说明:
# Recv-Q:当前全连接队列中的连接数
# Send-Q:全连接队列最大容量
# 示例输出:
# State Recv-Q Send-Q Local Address:Port
# LISTEN 0 128 *:8080
# LISTEN 129 128 *:3306 ← 这个有问题!Recv-Q > Send-Q
关键指标:如果Recv-Q接近或超过Send-Q,说明队列快满了或已经满了。
2. 查看半连接队列溢出
# 查看SYN_RECV状态的连接数
netstat -ant | grep SYN_RECV | wc -l
# 或者
ss -n state syn-recv | wc -l
如果数值异常高(比如几千),可能遭受SYN洪水攻击或半连接队列太小。
队列大小由什么决定
全连接队列大小
全连接队列大小 = min(backlog, somaxconn)
- backlog:应用程序listen()时指定
- somaxconn:系统参数
net.core.somaxconn
# 查看somaxconn
cat /proc/sys/net/core/somaxconn
# 默认通常是128,太小了
# 临时修改
sysctl -w net.core.somaxconn=65535
# 永久修改(写入/etc/sysctl.conf)
net.core.somaxconn = 65535
常见应用的backlog配置:
# Nginx
listen 80 backlog=65535;
# Tomcat server.xml
<Connector port="8080" acceptCount="1000" />
# Spring Boot
server.tomcat.accept-count=1000
半连接队列大小
# 查看
cat /proc/sys/net/ipv4/tcp_max_syn_backlog
# 修改
sysctl -w net.ipv4.tcp_max_syn_backlog=65535
实战案例:排查Connection refused
场景复现
某服务高峰期大量报错:Connection refused,但服务进程正常运行。
排查步骤
Step 1:确认服务在监听
ss -tlnp | grep 8080
# LISTEN 0 128 *:8080 users:(("java",pid=12345,fd=10))
服务正常监听,排除进程挂掉的可能。
Step 2:检查全连接队列
ss -lnt | grep 8080
# LISTEN 128 128 *:8080
# Recv-Q=128, Send-Q=128,队列已满!
Step 3:确认溢出
netstat -s | grep -i "listen"
# 123456 times the listen queue of a socket overflowed
数值还在增长,确认是全连接队列溢出。
Step 4:定位原因
队列满说明应用程序accept不够快,可能原因:
- 应用处理太慢(GC、锁竞争等)
- 线程池满了
- somaxconn太小
Step 5:解决
# 1. 调大系统参数
sysctl -w net.core.somaxconn=65535
# 2. 调大应用backlog(Tomcat为例)
# server.xml: acceptCount="65535"
# 3. 增加处理线程
# server.xml: maxThreads="500"
# 4. 重启服务生效
TIME_WAIT过多问题
另一个常见问题:TIME_WAIT状态连接过多,占用端口资源。
查看TIME_WAIT数量
ss -s
# 或
netstat -ant | grep TIME_WAIT | wc -l
为什么会有大量TIME_WAIT
- TIME_WAIT是主动关闭连接的一方产生的
- 默认持续时间:2MSL(Linux下通常是60秒)
- 高并发短连接场景容易积累
优化方案
# /etc/sysctl.conf
# 1. 允许TIME_WAIT复用
net.ipv4.tcp_tw_reuse = 1
# 2. 快速回收(有风险,NAT环境慎用)
# net.ipv4.tcp_tw_recycle = 1 # 内核4.12后已删除
# 3. 增加本地端口范围
net.ipv4.ip_local_port_range = 1024 65535
# 4. 减少FIN_WAIT时间
net.ipv4.tcp_fin_timeout = 30
监控脚本
写个脚本定期监控队列状态:
#!/bin/bash
# tcp_queue_monitor.sh
echo "=== 全连接队列溢出统计 ==="
netstat -s | grep -i "listen"
echo ""
echo "=== 各端口队列情况 ==="
echo "State Recv-Q Send-Q Local:Port"
ss -lnt | tail -n +2
echo ""
echo "=== TIME_WAIT统计 ==="
ss -s | grep -i time-wait
echo ""
echo "=== 各状态连接数 ==="
ss -ant | awk '{print $1}' | sort | uniq -c | sort -rn
完整调优参数
# /etc/sysctl.conf
# 全连接队列
net.core.somaxconn = 65535
# 半连接队列
net.ipv4.tcp_max_syn_backlog = 65535
# SYN Cookie(防SYN攻击)
net.ipv4.tcp_syncookies = 1
# TIME_WAIT优化
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.ip_local_port_range = 1024 65535
# 连接跟踪表(高并发必调)
net.netfilter.nf_conntrack_max = 2097152
# 应用生效
sysctl -p
远程排查小技巧
如果问题服务器不在身边,需要远程登录排查,又没有公网IP,可以用组网工具直接连接。我用的星空组网,提前把服务器加入虚拟局域网,随时随地SSH上去抓数据,比临时开VPN方便。
总结
| 问题 | 表现 | 排查命令 | 解决方案 |
|---|---|---|---|
| 全连接队列满 | Connection refused | ss -lnt |
调大somaxconn和backlog |
| 半连接队列满 | 连接超时 | ss -n state syn-recv |
调大tcp_max_syn_backlog |
| TIME_WAIT过多 | 端口耗尽 | ss -s |
开启tw_reuse |
记住排查顺序:先看ss -lnt确认队列状态,再看netstat -s确认溢出,最后定位应用层原因。

浙公网安备 33010602011771号