Linux最大TCP连接数:是65535?
核心结论:单机能维护的TCP连接数从来就不取决于端口号。真正的瓶颈可能在文件描述符上限、临时端口耗尽、内存不足或内核参数限制——取决于你的业务场景是服务端还是客户端。
场景约束
业务背景:某高并发网关服务需要维护大量长连接到后端服务,在压测过程中连接数达到某个阈值后开始出现 connect: cannot assign requested address 错误。
系统版本:CentOS 7.9 / Linux 5.4 内核
限制条件:
- 服务运行在容器环境(Docker 20.10)中
- 网关服务作为客户端,向多个后端服务发起连接
- 连接类型为HTTP/1.1短连接,压测工具为wrk
影响面:所有到后端服务的请求全部失败,影响下游所有依赖方
现象与关键证据
压测命令与错误日志
# 使用wrk模拟客户端持续加压
wrk -t4 -c2000 -d60s --latency http://backend-cluster:8080/api
错误日志模式(来自nginx upstream配置):
2024/11/15 14:23:45 [error] 25671#25671: *1892344 connect() failed (99: Cannot assign requested address)
while connecting to upstream, client: 10.0.1.100, server: 0.0.0.0:80
关键证据:错误码是 99(EADDRNOTAVAIL),不是 98(EADDRINUSE)。
两者含义完全不同:
EADDRINUSE (98):地址已被占用,你试图bind的端口已经被使用EADDRNOTAVAIL (99):无法分配地址,没有可用端口可分配给这个连接
系统参数快照
# 文件描述符状态
$ cat /proc/sys/fs/file-nr
1344 0 8388608
# 格式:已分配数 / 已分配未使用数 / 系统最大值
# 用户ulimit
$ ulimit -n
1024
# 系统级file-max
$ cat /proc/sys/fs/file-max
8388608
# 临时端口范围(关键!)
$ sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768 60999
# 计算可用端口数:60999 - 32768 + 1 = 28232
# 当前TCP连接状态
$ ss -s
Total: 148 (kernel 1234)
TCP: 142 (estab 50, closed 87, orphaned 0, synrecv 0, timewait 87)
# 发现87个TIME_WAIT连接在堆积
排查路径
第一层:文件描述符是否耗尽
技术判断:如果 ulimit -n 只有1024,那单个进程最多只能打开1024个连接。但观察当前 file-nr 显示只用了1344,所以这里不是瓶颈。
# 验证单进程fd使用
$ ls /proc/$(pgrep -f nginx)/fd | wc -l
248
第二层:临时端口范围(这里是真正的瓶颈)
技术判断:服务端监听 0.0.0.0:80 时,每个连接占用一个文件描述符,不受端口数量限制。但网关作为客户端发起连接时,每个连接需要一个临时源端口。
# 查看连接到同一个后端IP:Port的连接数
$ ss -ant | awk '{print $4, $5}' | grep ':8080' | wc -l
28000+
# 查看可用临时端口
$ sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 32768 60999
当连接数超过28232时,系统无法分配新的临时端口,触发 EADDRNOTAVAIL。
方案取舍
方案一:扩展临时端口范围
# 临时生效
sysctl -w net.ipv4.ip_local_port_range="1024 65535"
# 永久生效
echo "net.ipv4.ip_local_port_range = 1024 65535" >> /etc/sysctl.conf
优点:立即生效,无需修改应用代码 缺点:会消耗更多端口资源,可能与其他服务冲突
方案二:启用TIME_WAIT端口重用
# 允许重用TIME_WAIT状态的端口
sysctl -w net.ipv4.tcp_tw_reuse=1
# 缩短FIN_WAIT_2超时
sysctl -w net.ipv4.tcp_fin_timeout=15
为什么不推荐 tw_recycle:它依赖时间戳选项,在NAT/LB环境下会导致部分客户端连接异常重建。这个参数在Linux 4.12后已被移除,但在旧系统上仍需注意。
方案三:调整文件描述符上限
# 调整用户级ulimit(临时)
ulimit -n 1000000
# 调整系统级nr_open(必须大于ulimit)
sysctl -w fs.nr_open=1100000
# 永久配置
cat >> /etc/security/limits.conf << 'EOF'
* soft nofile 1000000
* hard nofile 1000000
EOF
为什么不只调ulimit:如果不调 fs.nr_open,ulimit设置过大会报错。
$ ulimit -n 10000000
-bash: ulimit: open files: cannot modify limit: Operation not permitted
最终选择:组合方案
针对高并发网关服务(客户端角色),我推荐以下组合配置:
# /etc/sysctl.conf
fs.file-max = 2000000
fs.nr_open = 2000000
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 8192
net.netfilter.nf_conntrack_max = 1048576
取舍理由:
- 扩展
ip_local_port_range是解决客户端端口耗尽的直接手段 tcp_tw_reuse加速TIME_WAIT回收,增加可用端口周转率- 提高
fs.nr_open确保ulimit能设置到足够高 - 保留
nf_conntrack_max以防iptables规则影响性能
实施方案与验证结论
实施步骤
# 1. 备份原配置
cp /etc/sysctl.conf /etc/sysctl.conf.bak
# 2. 应用新配置
sysctl -p
# 3. 验证生效
sysctl net.ipv4.ip_local_port_range
# 期望输出:net.ipv4.ip_local_port_range = 1024 65535
# 4. 修改limits.conf
cat >> /etc/security/limits.conf << 'EOF'
* soft nofile 1000000
* hard nofile 1000000
nginx soft nofile 1000000
nginx hard nofile 1000000
EOF
# 5. 重启服务使ulimit生效
systemctl restart nginx
# 6. 验证ulimit(需要重新登录或重启进程)
sudo -u nginx bash -c 'ulimit -n'
验证结论
方法一:逐步加压测试
# 逐步增加并发,观察错误率
for c in 10000 30000 50000; do
echo "=== Testing with $c connections ==="
wrk -t8 -c$c -d10s --latency http://backend-cluster:8080/api 2>&1 | \
grep -E 'Latency|Requests|Socket errors|non-2xx'
done
修复前:连接数到28000左右开始出现大量 EADDRNOTAVAIL
修复后:连接数稳定在60000+ 无报错
方法二:监控指标对比
# 修复前
$ ss -s
Total: 28350 (kernel 1234)
TCP: 28342 (estab 120, closed 28215, orphaned 0, synrecv 0, timewait 28100)
# 修复后
$ ss -s
Total: 150 (kernel 1234)
TCP: 142 (estab 50, closed 87, orphaned 0, synrecv 0, timewait 87)
# TIME_WAIT从28100降到87,端口复用生效
方法三:确认瓶颈转移
# 确认不再是端口问题
$ sysctl net.ipv4.ip_local_port_range
net.ipv4.ip_local_port_range = 1024 65535
# 可用端口数:65535 - 1024 + 1 = 64512
# 确认ulimit已生效
$ sudo -u nginx bash -c 'ulimit -n'
1000000
边界条件
什么时候端口数真的是瓶颈
当且仅当以下条件同时满足时:
- 你是客户端角色——发起连接的一方
- 所有连接指向同一个目标 IP:Port
- 短连接且TIME_WAIT堆积
ip_local_port_range没有扩展
什么时候ulimit才是瓶颈
每个TCP连接需要占用一个文件描述符。默认的1024对于生产服务远远不够。
容器环境额外约束
在Docker环境中,文件描述符限制可能受多个层面限制:
# 检查容器内限制
docker exec
<container_id> cat /proc/sys/fs/file-nr
# 检查宿主机限制
cat /proc/sys/fs/file-max
# 检查Docker daemon配置
cat /etc/docker/daemon.json
{
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 8400000,
"Soft": 8400000
}
}
}
关键判断:如果容器内 ulimit -n 是1024,但宿主机 fs.file-max 是8388608,那瓶颈在容器的ulimit限制,需要用 --ulimit nofile=1000000:1000000 启动参数覆盖。
内存约束
每个TCP连接大约占用3KB-10KB内存(取决于协议栈buffer大小)。100万连接意味着至少需要3GB-10GB内存专门用于连接维护。
# 估算单个连接内存占用
$ cat /proc/sys/net/ipv4/tcp_rmem
4096 16384 4194304
# 最小/默认/最大 receive buffer
$ cat /proc/sys/net/ipv4/tcp_wmem
4096 16384 4194304
# 最小/默认/最大 send buffer
# 10000连接 × 32KB(buffer) ≈ 320MB
长连接 vs 短连接场景对比
| 场景 | 主要瓶颈 | 优化方向 |
|---|---|---|
| HTTP短连接 | 临时端口、TIME_WAIT | 连接复用、端口扩展、tw_reuse |
| WebSocket长连接 | 文件描述符、内存 | ulimit调高、内存规划 |
| gRPC长连接 | 文件描述符、内存 | 同上 |
| 数据库连接池 | 文件描述符、连接池上限 | 调整服务连接数限制 |
总结
| 瓶颈类型 | 典型症状 | 检查命令 | 解决方案 |
|---|---|---|---|
| ulimit过小 | too many open files |
ulimit -n |
调整 /etc/security/limits.conf |
| 端口耗尽 | cannot assign requested address |
ss -s 看TIME_WAIT |
扩展 ip_local_port_range + tcp_tw_reuse |
| 内核fd上限 | ulimit无法超过某个值 | cat /proc/sys/fs/nr_open |
sysctl -w fs.nr_open=xxx |
| 内存不足 | OOM killed | free -h |
增加内存或减少连接数 |
回到最初的问题:Linux最大TCP连接数是多少?
答案是:取决于你遇到的具体瓶颈是什么。
对于服务端,瓶颈通常是文件描述符;对于客户端,瓶颈通常是临时端口。不要用65535来回答这个问题,也不要用65535来指导系统调优。先定位瓶颈,再针对性优化。
我不推荐的做法:上来就改 ip_local_port_range,却不检查ulimit和内核参数。TCP连接数问题从来不是单一因素导致的。