linux tcp连接数受三重限制:ulimit -n(进程级文件描述符)、fs.file-max(系统级文件描述符总数)、ip_local_port_range(客户端端口范围)和nf_conntrack_max(连接跟踪表),需分别检查;实际瓶颈常为最小值或内存。

Linux 系统的 TCP 连接数不是由单一参数决定的,而是受文件描述符、本地端口范围、连接跟踪表三重限制共同约束。直接查 netstat 或 ss 的 ESTABLISHED 数量,只能看到当前连接,不是上限。
怎么查当前生效的连接数限制
真正起作用的是三个独立维度,必须分别检查:
-
ulimit -n:当前 shell 进程能打开的最大文件描述符数(每个 TCP 连接占 1 个) -
cat /proc/sys/fs/file-max:整个系统级最大文件描述符总数 -
sysctl net.ipv4.ip_local_port_range:决定了客户端可发起的连接上限(如32768 65535表示最多约 32768 个 outbound 连接) -
sysctl net.netfilter.nf_conntrack_max:NAT 或防火墙开启时,内核连接跟踪表容量(影响所有方向的连接)
注意:ulimit -n 值如果低于其他项,它就是实际瓶颈;很多服务(如 nginx、mongod)启动时会继承父进程的 ulimit,即使系统级调高了也没用。
为什么改了 limits.conf 还不生效
常见失效原因有三个:
- 没配
/etc/pam.d/common-session或/etc/pam.d/sshd(Ubuntu/Debian 系默认不用/etc/pam.d/login) - 服务是 systemd 启动的,但没在 service 文件里加
LimitNOFILE=,或没改/etc/systemd/system.conf中的DefaultLimitNOFILE - 用户登录后未新开 shell,
ulimit -n不会自动刷新;需重新登录或运行su - $USER
验证是否生效:先 su - $USER,再运行 ulimit -n —— 这才是服务实际拿到的值。
nf_conntrack_max 和 file-max 有什么区别
二者完全不重叠,但都卡连接数:
-
fs.file-max是系统总文件描述符池,TCP socket、普通文件、管道都算在内 -
net.netfilter.nf_conntrack_max是 netfilter 模块维护的连接状态表大小,仅当启用iptables规则、使用 NAT、或加载nf_conntrack模块时才起作用;纯直连服务器若没开 iptables,这个值再小也不影响连接建立 - 如果两者都调到 100 万,最终瓶颈通常是内存:每条 ESTABLISHED 连接在内核中至少占 3.3KB~20KB(取决于
tcp_rmem/tcp_wmem设置),4GB 内存撑满 100 万连接非常吃紧
改完参数后连接数还是上不去?重点看这三处
最容易被忽略的其实是服务自身限制和应用层行为:
- MySQL 默认
max_connections=151,MongoDB 默认maxConns=20000,不改配置文件或启动参数,内核调再高也没用 - Java 应用里
HttpClient或连接池(如 HikariCP)有自己最大连接数,常比系统限制低得多 - 客户端快速断连造成大量
TIME_WAIT,占用端口和连接跟踪表;可通过net.ipv4.tcp_tw_reuse=1缓解,但不能滥用在 NAT 环境
真正压测前,建议用 ss -s 看 summary,它会同时显示 sockets: used(已用文件描述符)、tcp:(各状态连接数)、memory:(内核 socket 内存用量)—— 这三项比任何单个参数都更能定位瓶颈点。











