相机MQTT遗嘱消息发送异常分析

2021-08-04  本文已影响0人  华木公子

一)异常说明
出现的异常现象是:

1、有时候相机端断电再上电后, 平台端会先收到相机上线消息,接着收到相机下线消息, 导致平台获取的相机状态是错误的——相机已上电但平台认为其下线;

2、有时候相机断电后可以收到相机遗嘱消息,有时候相机断电后一直都收不到相机遗嘱消息;

3、调试时发现emqtt发送遗嘱消息抛异常;

二、一些概念:
当前MQTT协议版本好为V5;
emqtt,在本文指的时mqtt服务器;
broker, emqtt中的消息控制器,用于消息协调,是emqtt服务器中枢;
client,在本文指的是连接 emqtt 的客户端,包括 消息发送方(相机端) 和 消息订阅方(平台端)
topic, 消息主题, emqtt中有各种各样的topic, 各个client向不同的 topic 发布消息或者订阅消息,从而达到消息的传递和监听;
session,指的是emqtt针对client的连接创建一个会话,该会话代表这个client,一个会话可以对应一个topic,一个topic可以对应多个session
message, 指的是 client 和 emqtt 之间发布或订阅的消息
willMessage, 指的是消息发布client(相机端)离线后, broker 会基于相机端预先注册的遗嘱,把遗嘱当作消息发布到对应 topic 上;

MQTT消息,有几个重要的属性

cleansession    
是否复用同一session。 v3.1.1协议, 连接参数。
如果 Clean Session 设置为 0,服务端必须使用与 Client ID 关联的会话来恢复与客户端的通信。如果不存在这样的会话,服务器必须创建一个新会话。客户端和服务器在断开连接后必须存储会话的状态。
如果 Clean Session 设置为 1,客户端和服务器必须丢弃任何先前的会话并创建一个新的会话。该会话的生命周期将和网络连接保持一致,其会话状态一定不能被之后的任何会话重用。

如果重新连接后emqtt用同一session,那么就可以client就可以读取对应session中因为离线而错过的消息、未确认相应的消息(qos=1,qos=2);如果重新连接后emqtt用新的session,新连接就只能获取到新的消息而无法获取到离线期间的消息,同时原来session就会遗留在emqtt内部,直到该session自动超期。 setCleansession(false);cleansession = 0
clean start 连接时是否关联已有会话。V5.0协议。
如果 CONNECT 报文中的 Clean Start 为 1,客户端和服务端必须丢弃任何已存在的会话,并开始一个新的会话。
如果 CONNECT 报文中的 Clean Start 为 0 ,并且存在一个关联此客户端标识符的会话,服务端必须基于此会话的状态恢复与客户端的通信。如果不存在任何关联此客户端标识符的会话,服务端必须创建一个新的会话。

只在V5中生效,与V3的cleansession功能大致相同,一般配合session expire interval 使用   setCleanstart(false);cleanstart = 0
session expire interval 
Session Expiry Interval 以秒为单位,如果 Session Expiry Interval 设置为 0 或者未指定,会话将在网络连接关闭时结束。
如果 Session Expiry Interval 为 0xFFFFFFFF ,则会话永不过期。
如果网络连接关闭时(DISCONNECT 报文中的 Session Expiry Interval 可以覆盖 CONNECT 报文中的设置) Session Expiry Interval 大于0,则客户端与服务端必须存储会话状态 。

只在V5中生效,一般配合 clean start 使用,可以让emqtt按照client的时间要求持久化session会话(v3版本无法设置session会话持久化时间,导致资源的浪费)   
qos 消息质量(包括遗嘱消息),broker在投递消息时,需要根据qos的值来确认投递结果,此投递包括消息发布和消息监听:qos = 0, 最多只发一次,即只管消息发送,发出去就不管了;qos = 1, 至少发送一次,即至少要收到接收端的一个ack相应,如果在一定时间内收不到,就持续发送,因此,有可能在client没有及时响应ack的情况下,会发送多次,导致接收端收到重复的消息。qos = 2, 发送一次且至多发送一次, 即消息的高质量送达,broker会在消息发送之外还会和client进行多次消息交互握手确认,确保消息送达且只送达一次。一个消息会从发送到订阅的传递,当发送端设置的qos 和 订阅端的 qos 不一致时,broker 按qos最小值进行处理, 如 发送消息设定的qos = 2, 订阅消息的 qos = 0,则最终broker按 qos = 0投递消息;  qos值越高,需要的网络开销越大(交互越多)同时对网络的依赖也越大,因为 qos=1 和 qos=2 都依赖于broker和client之间的i交互确认。   qos = 0
retained    
消息是否保留。 任何一个消息(包括遗嘱消息),都有此选项。一个topic可以有且仅有一个保留消息,新的保留消息会覆盖老的保留消息;保留消息不存储在session中,而是又broker单独为每个topic单独保存;  保留消息的好处,当有新的连接订阅该topic时,emqtt会先发送保留消息给该连接,从而确保该连接立刻获取到topic上的消息。比如,一个数据三小时发布到topiic一次,如果一个连接在该数据刚发布才订阅该topic,就导致三个小时一直没有数据;如果最近的一次数据在topic被保留,那么该连接就可以获取最近的一次数据,优化体验。保留消息的坏处,由于保留消息会一直存在,导致新的连接都会获得该消息,如果该消息不是它想要的,就会存在污染。因此,一些人的做法就是让发送方重新发送一个空的保留消息(playboy),覆盖原有保留消息即可。  

三)异常分析
当前系统的一些参数设置如下:
相机端发送上线消息的连接参数是 qos=1,clean start = 1, retained = true;
相机端注册的遗嘱消息的参数是 qos=1,retained = false;
平台订阅的消息的参数是 qos=2
相机端发送上线消息 和 注册的遗嘱消息 的主题是同一个主题 aTopic/xxxx;
一个相机对应一个上述 aTopic/xxxx
平台端订阅该主题消息: qos = 1

1
------问题
为什么有时候 会出现 相机端断电再上电后,平台端先收到该相机上线消息,接着收到相机下线消息?
------分析
相机端设置的上线消息 cleanstart = 1,即每次连接都broker都使用新的session来连接(原有session被废弃),此时,新的session发布上线消息,平台端收到此消息,相机上线。然后原session经过一定时间后心跳超时,borker就会发送遗嘱消息给平台端,于是平台端又收到了相机下线消息。
解决方案
相机端注册发送上线连接时,设置 cleanstart = 0. 就保证了每次相机端的连接时复用已有session
若相机上电时,该session心跳还未超期,就不会发送遗嘱消息,平台端就只会收到相机上线消息(这与华夏测试的现象一致,修改 cleanstart=0之后,在心跳间隔内重新上电,不会发送遗嘱消息,消息正确);

若相机上电时,该session心跳已经超期,则broker已向 aTopic 发送遗嘱消息,然后才发送上线消息,平台也是先收到相机下线消息,然后收到相机上线消息(这与华夏测试的现象一致,修改 cleanstart=0之后,在心跳间隔后重新上电,消息接收顺序正确);

2
------问题
为什么之前测试相机断电再上电后,没有出现平台先收到上线消息然后收到下线消息现象?
-----分析
在相机端 cleanstart = 1时,若断电和上电的时间间隔超过broker中session的心跳监控时长,则broker先发遗嘱消息,再发上线消息,就不会出现异常现象; 若断电和上电的时间间隔小于session心跳监控时长,但session心跳设置得很长,导致broker先收到上线消息,(很久之后)才收到遗嘱下线消息,若在收到遗嘱消息之前停止测试,则也会认为没有异常现象。
------解决方案
相机端注册发送上线连接时,设置 cleanstart = 0 即可;

3
-----问题
为什么修改遗嘱消息的 qos =0,retained = true 时,就不会出现先收到上线消息再收到下线消息现象?而在qos=1时,debug会抛异常?
-----分析
(此分析存疑)——遗嘱消息为相机端离线后,session超过心跳监控时长,broker向 aTopic 发送的消息,qos=0 说明只发送不管结果,retained=false 表明发送后不再保留该消息。当遗嘱消息qos设置为1时,是需要平台端session反馈才能收到消息。因为平台使用的是自研netty框架来实现的mqtt服务器,netty是多路复用机制支持高并发的数据传输。在未特别设置情况下,session也是不持久化数据的。当broker发送遗嘱消息到topic时:
如果遗嘱消息qos=1(发送qos=1,订阅qos=2,则最终qos=1),则需要ack响应,但由于订阅session未复用(平台使用新的session订阅),原有session就会因为ack超时而抛异常——就出现了debug时的异常信息;
如果遗嘱消息qos=0(发送qos=0,订阅qos=2,则最终qos=0),retained = true,则遗嘱消息会保留下来,并且不存在因为订阅session超时而无法获取ack抛异常情况,同时新的订阅session也能获取到保留消息,从而得到相机下线消息;
所以,当修改遗嘱消息的 qos = 0, retianed = true 也可规避此异常现象;
-------解决方案
遗嘱消息的 qos=0, retained = true

4
------问题
为什么有时会出现相机下线后,等了很久(已超过心跳检测间隔)平台端也没有收到遗嘱消息?
------分析
遗嘱消息qos=1(发送qos=1,订阅qos=2,则最终qos=1),retained=false; 则当broker发送遗嘱给topic后不再保留遗嘱消息,就有一定可能导致平台订阅端的新的session无法获取到topic保留的遗嘱消息(因为平台订阅端的session未复用)。

5
----问题
为什么上线消息一直没有出现问题,平台端都能收到?
----分析
因为上线消息的 retained = true,无论qos等于多少,订阅方的session都能收到此上线消息;

   四)最终解决方案如下:
   第1点) 相机端注册连接时,clean start = 0;
   第2点)同时由于我司mqtt服务器和平台订阅端高速内网,遗嘱消息 qos=0,提高效率;
   第3点) 同时,为保证我司所有新的订阅session都能收到保留的遗嘱消息,遗嘱消息 retained = true;
上一篇下一篇

猜你喜欢

热点阅读