在近期项目设备联调过程中,我们遇到了一个令人困惑的MQTT连接问题。设备虽然能够连接至MQTT服务器,但表现出连接不稳定的现象:数据无法正常上报,设备不断重连,且日志显示设备在重启前反复订阅主题。以下是问题定位与解决的完整过程,希望能为类似问题的排查提供参考。

问题描述

设备连接至MQTT服务器后,串口日志显示在重复订阅主题,但服务器迟迟未响应确认(ACK),导致设备每隔5秒重试订阅主题。从串口日志可见,消息的ACK响应延迟约为10秒(如图所示),明显与预期不符。
notion image
 

日志观察

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

引出疑问

这种延迟可能涉及多方面的原因:
  • 是设备端订阅处理的速度较慢导致的?
  • 还是网络环境不佳,引发了数据传输的延迟?
  • 抑或是IT部门在NAT端口转发的配置上存在特定问题,使外网访问时产生了10秒的延迟?
想要得到确切原因,需要通过进一步的分界与深入分析。
 

问题定位

为明确问题根源,我们通过抓包和日志分析逐步缩小问题范围,采用以下手段:

1. 抓包分析

在设备连接的路由器上抓取TCP包,观察到设备是否能够正常连接服务器并发送多个MQTT订阅请求,确认设备端的网络与固件没有问题。
Wireshark抓包数据显示,设备在建立TLS连接后正常发送了16个MQTT订阅包(如图所示)。
notion image
 

2. 服务端日志深挖

notion image
 
将EMQX日志级别调整为DEBUG后,发现设备触发了EMQX的限流机制,导致服务器无法及时处理瞬时的订阅请求,延迟了ACK响应。
notion image
 

问题分析

通过对EMQX配置文件的深入研究,我们发现了关键配置项:
上述配置限制了每秒的订阅消息处理速率。当设备连接后,短时间内发起了多个订阅请求,导致触发限流,服务器推迟了ACK的处理。
 

问题解决

1. 修改限流配置

根据设备的实际情况,适当放宽或移除限流限制。调整后的配置如下:
 

2. 检查配置生效情况

当改了配置重启EMQX服务后,发现问题依旧。
经过深入研究EMQX相关工具使用,发现可以使用下面指令查看到配置的变更情况。
 
检查官方关于配置文件的说明,发现在 data/configs/cluster-override.conf 下还有集群配置
集群配置与emqx.conf配置冲突,导致修改了emqx.conf后配置不生效。删除集群配置再重启服务,查看配置变更,这时候发现配置生效了。

验证与结果

经过上述配置调整与验证,设备端的订阅请求得到了正常的ACK响应,串口日志显示设备连接稳定,不再重复订阅,问题得以解决。
 

源码分析

EMQX 的限流逻辑用于控制消息处理的速率,防止系统过载。它通过令牌桶算法来限制操作或消息在特定时间内的数量。当操作请求超出限流器的令牌容量时,系统会暂停该操作,等待令牌恢复后再重试。
  1. 源码路径: 限流的关键入口位于文件 emqx_connection.erl,具体代码如下:
    1. 辅助模块调用: 限流逻辑的实现依赖多个辅助模块,包括 emqx_limiter_containeremqx_htb_limiter,负责具体的令牌校验、暂停时间计算等操作。例如:
        • emqx_limiter_container:check_list/2 检查多个限流器是否满足条件。
        • emqx_htb_limiter:check/2 验证请求所需令牌数量是否足够。
    1. 令牌校验实现: 在 emqx_htb_limiter.erl 中,令牌校验的核心逻辑如下:
      1. 重试上下文设置: 当触发限流时,系统会通过 emqx_limiter_container:set_retry_context/2 设置重试上下文,确保在令牌恢复后能够继续未完成的操作。
       

      拓展阅读

       
       
      深入实测 EMQX 限流机制高效调试物联网通信:MQTTX与在线Protobuf解码工具的应用详解
      Loading...