在近期项目设备联调过程中,我们遇到了一个令人困惑的MQTT连接问题。设备虽然能够连接至MQTT服务器,但表现出连接不稳定的现象:数据无法正常上报,设备不断重连,且日志显示设备在重启前反复订阅主题。以下是问题定位与解决的完整过程,希望能为类似问题的排查提供参考。
问题描述
设备连接至MQTT服务器后,串口日志显示在重复订阅主题,但服务器迟迟未响应确认(ACK),导致设备每隔5秒重试订阅主题。从串口日志可见,消息的ACK响应延迟约为10秒(如图所示),明显与预期不符。

日志观察
- 设备端日志: 设备未能及时收到订阅ACK,且在5秒内重新发起订阅。
- 示例日志显示:消息ID 23968的订阅消息没有收到及时的ACK,而是10s后才收到对应ACK。
- EMQX服务器端日志: 对设备连接的客户端ID进行追踪后,发现订阅主题的消息确实延迟到10秒后才被处理。
- 示例日志记录了从SUBSCRIBE到SUBACK的延迟。

使用的MQTT brocker 为EMQX,版本为v5.0.13
引出疑问
这种延迟可能涉及多方面的原因:
- 是设备端订阅处理的速度较慢导致的?
- 还是网络环境不佳,引发了数据传输的延迟?
- 抑或是IT部门在NAT端口转发的配置上存在特定问题,使外网访问时产生了10秒的延迟?
想要得到确切原因,需要通过进一步的分界与深入分析。
问题定位
为明确问题根源,我们通过抓包和日志分析逐步缩小问题范围,采用以下手段:
1. 抓包分析
在设备连接的路由器上抓取TCP包,观察到设备是否能够正常连接服务器并发送多个MQTT订阅请求,确认设备端的网络与固件没有问题。
Wireshark抓包数据显示,设备在建立TLS连接后正常发送了16个MQTT订阅包(如图所示)。

2. 服务端日志深挖

将EMQX日志级别调整为
DEBUG后,发现设备触发了EMQX的限流机制,导致服务器无法及时处理瞬时的订阅请求,延迟了ACK响应。
问题分析
通过对EMQX配置文件的深入研究,我们发现了关键配置项:
上述配置限制了每秒的订阅消息处理速率。当设备连接后,短时间内发起了多个订阅请求,导致触发限流,服务器推迟了ACK的处理。
问题解决
1. 修改限流配置
根据设备的实际情况,适当放宽或移除限流限制。调整后的配置如下:
或
2. 检查配置生效情况
当改了配置重启EMQX服务后,发现问题依旧。
经过深入研究EMQX相关工具使用,发现可以使用下面指令查看到配置的变更情况。
检查官方关于配置文件的说明,发现在 data/configs/cluster-override.conf 下还有集群配置
集群配置与emqx.conf配置冲突,导致修改了emqx.conf后配置不生效。删除集群配置再重启服务,查看配置变更,这时候发现配置生效了。
验证与结果
经过上述配置调整与验证,设备端的订阅请求得到了正常的ACK响应,串口日志显示设备连接稳定,不再重复订阅,问题得以解决。
源码分析
EMQX 的限流逻辑用于控制消息处理的速率,防止系统过载。它通过令牌桶算法来限制操作或消息在特定时间内的数量。当操作请求超出限流器的令牌容量时,系统会暂停该操作,等待令牌恢复后再重试。
- 源码路径:
限流的关键入口位于文件
emqx_connection.erl,具体代码如下:
- 辅助模块调用:
限流逻辑的实现依赖多个辅助模块,包括
emqx_limiter_container和emqx_htb_limiter,负责具体的令牌校验、暂停时间计算等操作。例如: emqx_limiter_container:check_list/2检查多个限流器是否满足条件。emqx_htb_limiter:check/2验证请求所需令牌数量是否足够。
- 令牌校验实现:
在
emqx_htb_limiter.erl中,令牌校验的核心逻辑如下:
- 重试上下文设置:
当触发限流时,系统会通过
emqx_limiter_container:set_retry_context/2设置重试上下文,确保在令牌恢复后能够继续未完成的操作。
拓展阅读
- 作者:Yibin
- 链接:https://yibin.dev/article/14860b50-99a4-802b-baf0-d9c43f9cc219
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章









