必须设为1048576,因EMQX每个MQTT长连接至少占2个文件描述符,百万连接需200万fd,而默认ulimit -n仅1024,不调则触发EMFILE错误导致新连接全拒。

为什么 ulimit -n 必须设到 1048576
EMQX 每个 MQTT 长连接至少占用 2 个文件描述符(TCP socket + Erlang port),百万连接意味着至少 200 万 fd。但 Linux 进程级限制 ulimit -n 默认只有 1024,不改就直接报 accept: EMFILE 错误,新连接全被拒绝。
必须同时设置三处:
-
/etc/security/limits.conf中添加:* soft nofile 1048576和* hard nofile 1048576 -
/etc/systemd/system.conf中设置:DefaultLimitNOFILE=1048576(否则 systemd 启动的 emqx 服务会继承默认值) - Docker Compose 的
ulimits.nofile必须显式写为1048576,不能只靠宿主机 limits
net.core.somaxconn 和 tcp_max_syn_backlog 怎么配才不丢连接
这两个参数控制 TCP 连接建立阶段的队列长度。当客户端并发建连(比如每秒 1000 个新连接),如果队列太小,SYN 包会被内核直接丢弃,现象是客户端 connect timeout 或 RST,而 EMQX 日志里看不到任何接入痕迹。
推荐值(缺一不可):
-
net.core.somaxconn = 32768:全连接队列上限(已完成三次握手、等待 accept() 的连接) -
net.ipv4.tcp_max_syn_backlog = 16384:半连接队列上限(收到 SYN、未完成握手的连接) -
net.core.netdev_max_backlog = 16384:网卡收包队列,防止高并发时软中断丢包
注意:这些必须写进 /etc/sysctl.conf 并执行 sysctl -p 生效;Docker 容器内无法覆盖,得在宿主机调。
tcp_rmem/wmem 设置太大反而导致内存 OOM
EMQX 大量长连接下,每个 socket 的读写 buffer 累积起来非常可观。把 net.core.rmem_max 设成 16777216(16MB)看似合理,但实际每个连接未必用满——多数 MQTT 心跳包只有几字节,却仍按 max 分配页框,极易触发 OOM Killer 杀掉 emqx 进程。
更稳妥的做法是收紧默认值,靠动态扩缩容:
-
net.core.rmem_default = 262144(256KB) net.core.wmem_default = 262144-
net.ipv4.tcp_rmem = "1024 4096 262144"(min/default/max) net.ipv4.tcp_wmem = "1024 4096 262144"
这样既满足突发消息,又避免静态分配浪费;实测 100 万连接下内存节省 12GB+。
nf_conntrack_max 不设够会导致连接被静默重置
Linux 默认 nf_conntrack_max 只有 65536,远低于百万连接需求。一旦打满,内核 netfilter 会开始丢弃新连接的 conntrack entry,表现为客户端偶发 RST、重连失败,EMQX 日志无异常,ss -s 却显示大量 tw 状态连接堆积。
必须设置:
net.nf_conntrack_max = 1000000net.netfilter.nf_conntrack_max = 1000000-
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30(缩短 TIME-WAIT 持续时间)
注意:net.ipv4.tcp_tw_reuse 和 tcp_tw_recycle 在 NAT 环境下禁用,否则设备端可能收不到 ACK,直接断连——这点最容易被忽略,且故障难复现。


















