环境
- EMQX 版本:emqx-5.0.19-el7-amd64、emqx-5.0.26-el7-amd64
- 操作系统版本:centos 7
重现此问题的步骤
- 客户端1订阅系统主题$SYS/brokers/+/clients/+/disconnected和$SYS/brokers/+/clients/+/connected 并连接上mqtt broker
- 客户端2 ,连接设置 :“clean_start”:false “expiry_interval”:600000000 ,第一次连接上mqtt broker,然后断开
- 客户端2再次连接上mqtt,此时客户端1会收到客户端2先disconnected然后connected两条消息
如下:
预期行为
客户端连接到mqtt后,订阅客户端上下线消息的客户端不应该收到disconnected 消息,而应该只收到connected消息
实际行为
客户端连接mqtt broker后,订阅客户端上下线消息的客户端会先收到disconnected消息,然后才收到connected消息
因为你设置了 expiry_interval,所以在步骤 2 结束后,EMQX 内依然保留着会话,当你再次登录时,上一个会话就会被新的连接接管, reason:takenover 产生一次断开消息。
上下线消息不是指 TCP/SSL 连接的产生和断开,而是指会话的产生和关闭
但是这样的设计对监控客户端的上下线就变得异常困难,两个消息同时发送过来,我们服务器对disconnected和connected消息处理的先后顺序没法保证,特别是在多线程处理消息的情况下,很有可能disconnected消息后处理,最后判断设备的在线状态就是离线了,请问我该怎么解决这个问题呢
另外,我们测试了,emqx-5.0.8-el7-amd64这个版本,在相同的条件下,设备连接mqtt,是不会有disconnected的消息的,这个是后面的版本做了变更吗
stone
4
connect 和 disconnect 事件是从不同的 Erlang 进程独立发出来的,为了优先处理 重连,通常情况下 重连的 connected 事件会先于就连接的disconnected 事件。
想要通过这些事件来在外部重建客户端在线/离线状态的话,有2个方案可以考虑:
- 过滤掉 reason = takenover 或者 reason = discarded 的 disonnect 事件。
- 判断 connected_at 和 disconnected_at 时间戳,在最新的 5.10.4 和 6.0.3, 6.1.1 以及后续版本中,重连情况下,即使是 disconnected 事件在connected事件后发出,disconnected_at 也会总是比 connected_at 要早。