连接池打满的典型现象是设备大面积断线重连但emqx日志无网络错误,仅见“no available connection in pool”或“pool exhausted”;确认方式包括检查ss与emqx_ctl clients数量差异、查看cluster_links池使用率及排查僵尸连接。

连接池打满的典型现象与确认方式
设备大面积断线重连,但 EMQX 日志里没有明显网络错误(如 tcp_closed、connection refused),反而频繁出现 no available connection in pool 或 pool exhausted 类似提示——这基本就是连接池打满了。更隐蔽的情况是日志里只看到大量 client disconnected due to keepalive timeout,但实际客户端心跳正常,此时要怀疑连接池已无法分配新连接,导致后续连接请求被阻塞或超时丢弃。
emqx_cluster_link 连接池打满的特殊性
如果你启用了集群链接(Cluster Linking),emqx_cluster_link_mqtt 模块会为每条链接维护独立的 MQTT 客户端连接池。它不共享全局连接池,也不受 zone.external.max_connections 控制。池大小由配置项 cluster_link.mqtt.pool_size 决定,默认仅 10。当跨集群转发消息量突增(比如某类 Topic 突发高吞吐),这个池子会最先耗尽,表现为目标集群收不到消息,本集群客户端却持续断连——因为转发链路卡死,导致本地连接状态异常。
- 检查命令:
emqx_ctl cluster_links list查看各链接的mqtt_pool_used和mqtt_pool_size - 关键配置路径:
etc/emqx.conf中的cluster_link.mqtt.pool_size,建议按预期并发链接数 × 2~3 设置(例如 5 条链接 × 20 = 100) - 注意:该参数修改后需重启链接(
emqx_ctl cluster_links delete <name></name>+ 重建),不是热生效
普通 MQTT 客户端连接池打满的排查要点
EMQX 的客户端连接本身也依赖底层 TCP 连接池(emqx_connection 模块管理)。打满通常不是“池子小”,而是连接未及时释放。常见诱因有:
- 客户端未正确发送
DISCONNECT包就直接断网(比如 Android App 进入后台被系统杀进程),EMQX 等待tcp_idle_timeout(默认 30s)才清理连接,期间该连接一直占着池位 -
zone.external.max_connections设得太低,而设备数或重连风暴超出上限;注意该值是硬限制,超限后新连接会被静默拒绝 - 防火墙/NAT 设备回收了空闲连接,但 EMQX 还没检测到断开(TCP Keep-Alive 未启用或间隔过长),连接处于
ESTABLISHED假死状态,持续占用池资源
验证方法:用 ss -tn state established | grep :1883 | wc -l 对比 emqx_ctl clients list | wc -l,若前者远大于后者,说明存在大量僵尸连接。
为什么调大池子不总能解决问题
盲目增大 pool_size 或 max_connections 可能掩盖真实问题,甚至引发雪崩。Erlang VM 的调度器和 socket 资源消耗随连接数非线性增长;超过物理内存或文件描述符限制后,EMQX 会开始 GC 频繁、runq_overload 告警、消息延迟飙升。真正要盯的是连接生命周期:是否所有设备都设置了合理 keepalive(建议 60–120s),服务端 tcp_idle_timeout 是否 ≥ keepalive × keepalive_multiplier(至少 1.5 倍),以及客户端是否在断网前主动 DISCONNECT。











