环境
- EMQX 版本:v5.8.1
- 操作系统版本:macOS 15.1
重现此问题的步骤
- 打开mqtt-sn网关
- 使用 mqtt-sn-tools 订阅随便一个主题并建立起一个客户端连接成功
- 当客户端keep alive心跳设置为60秒,超过60秒钟后并没有收到网关的PINGRESP包
- 就算设置空闲超时时间到更高的数值也没用
- 无法正确显示客户端的心跳
预期行为
mqtt-sn网关遵从设置的空闲超时时间值,并且正确客户端显示客户端的心跳值
mqtt-sn网关遵从设置的空闲超时时间值,并且正确客户端显示客户端的心跳值
可以发一下 emqx 侧的全部 debug 日志么。
方便打开 debug 么 dashboard -》 managment =》 log
谢谢,看起来是个 bug(而且不简单)。会在下周一开例会排期来排查哈。
请问有任何进展吗
已经排期了,应该安排在 5.9.0 的。
感谢反馈,这里的几个问题:
空闲超时(IdleTimeout):是用来控制网关在Socket创建后【多长时间未收到第一个报文】就是否链接资源。它和这里的心跳逻辑没有关系的。
页面 Keepalive 始终显示为0,这个是 BUG。
当 mqtt-sn-sub 的心跳设置为 60s 后,客户端报类似以下日志(我本地重现的):
2025-01-08 10:54:46 ERROR Keep alive error: timed out while waiting for a PUBLISH from gateway.
服务端报(从帖子的 log.zip 文件里找到的):
2024-11-14T10:57:31.471993+08:00 [warning] msg: receive_unknown_packet_in_idle_state, peername: 192.168.107.1:44473, packet: {mqtt_sn_message,22,<<>>}
通过观察服务器的日志可以发现 mqtt-sn-sub 在创建连接和发送心跳时使用不动的源端口
2024-11-14T10:56:31.425170+08:00 [debug] msg: RECV_packet, peername: 192.168.107.1:50898, packet: SN_CONNECT(W0, C1, ProtocolId=1, Duration=60, ClientId=macbook)
连接使用的源端口是 50898,发心跳用的 44473 从而导致了服务器无法识别心跳报文。
问题3解决办法:
固定客户端消息发送端口。这在简单网络下有用,例如客户端直连服务器,但在在复杂的网络下,例如经过中间的网络设备,NAT 转换等,服务端拿到的客户端端口也是由中间的网络设备维护的,也是可能会变化的。一旦 NAT 映射关系被回收,那么上行的心跳会错误,下行的报文也无法抵达了。
将心跳时间改短,例如 10s。如果业务上,这个客户端需要实时关注消息,那应该把心跳设定的更短,以维持在复杂网络上的网络链路保活
我好像找到了问题所在,这种问题只会出现在docker部署emqx的情况下:
在任务管理器中查看得到mqtt-sn-tool的mqtt-sn-sub真实占用的端口是65344。
docker network的网关是192.168.97.1
在emqx容器的log中查看客户端的地址发现被网关的地址隐藏了,如图192.168.97.1:26180
超过60秒后,在客户端发送PINGREQ心跳的时候端口又会被改变一次成192.168.97.1:35768
超过60秒后客户端口63161依然不会改变
问题不在mqtt-sn-tools改变发心跳端口号,而是在docker容器上?我该如何配置呢
如果业务要求心跳必须超过60s以节省带宽怎么办呢?还有任何的解决方案吗?