MQTT协议笔记之消息流
2015-07-13 11:21
537 查看
前言
前面的笔记已把所有消息类型都过了一遍,这里从消息流的角度尝试解读一下。网络故障
在任何网络环境下,都会出现一方连接失败,比如离开公司大门那一刻没有了WIFI信号。但持续连接的另一端-服务器可能不能立即知道对方已断开。类似网络异常情况,都有可能在消息发送的过程中出现,消息发送出去,就丢失了。MQTT协议假定客户端和服务器端稳定情况一般,彼此之通信管道不可靠,一旦客户端网络断开,情况就会很严重,很难恢复原状。
但别忘记,很多客户端会有永久性存储设备支持,比如闪存ROM、存储卡等,在通信出现异常的情况下可以用于保存关键数据或状态信息等。
总之,异常网络情况很复杂,只能小心处理之。
消息重发策略
在QoS > 0情况下,PUBLISH、PUBREL、SUBSCRIBE、UNSUBSCRIBE等类型消息在发送者发送完之后,需要等待一个响应消息,若在一个指定时间段内没有收到,发送者可能需要重试。重发的消息,要求DUP标记要设置为1.等待响应的超时应该在消息成功发送之后开始算起,并且等待超时应该是可以配置选项,以便在下一次重试的时候,适当加大。比如第一次重试超时10秒,下一次可能为20秒,再一次重试可能为60秒呢。当然,还要有一个重试次数限制的。
还有一种情况,客户端重新连接,但未在可变头部中设置clean session标记,但双方(客户端和服务器端)都应该重试先前未发送的动态消息(in-flight messages)。客户端不被强制要求发送未被确认的消息,但服务器端就得需要重发那些未被去确认的消息。
QoS level决定的消息流
QoS level为Quality of Service level的缩写,翻译成中文,服务质量等级。MQTT 3.1协议在"4.1 Quality of Service levels and flows"章节中,仅仅讨论了客户端到服务器的发布流程,不太完整。因为决定消息到达率,能够提升发送质量的,应该是服务器发布PUBLISH消息到订阅者这一消息流方向。
QoS level 0
至多发送一次,发送即丢弃。没有确认消息,也不知道对方是否收到。Client | Message and direction | Server |
---|---|---|
QoS = 0 | PUBLISH----------> | Action: Publish message to subscribers then Forget Reception: <=1 |
网络层面,传输压力小。
QoS level 1
所有QoS level 1都要在可变头部中附加一个16位的消息ID。SUBSCRIBE和UNSUBSCRIBE消息使用QoS level 1。
针对消息的发布,Qos level 1,意味着消息至少被传输一次。
发送者若在一段时间内接收不到PUBACK消息,发送者需要打开DUB标记为1,然后重新发送PUBLISH消息。因此会导致接收方可能会收到两次PUBLISH消息。针对客户端发布消息到服务器的消息流:
Client | Message and direction | Server |
---|---|---|
QoS = 1 DUP = 0 Message ID = x Action: Store message | PUBLISH----------> | Actions: Store message Publish message to subscribers Delete message Reception: >=1 |
Action: Discard message | PUBACK<---------- | Message ID = x |
Server | Message and direction | Subscriber |
---|---|---|
QoS = 1 DUP = 0 Message ID = x | PUBLISH----------> | Actions: Store message Make message available Reception: >=1 |
PUBACK<---------- | Message ID = x |
在业务层面,或许可以弥补MQTT协议的不足之处:重试的消息ID一定要一致接收方一定判断当前接收的消息ID是否已经接受过
但一样不能够完全确保,消息一定到达了。
QoS level 2
仅仅在PUBLISH类型消息中出现,要求在可变头部中要附加消息ID。级别高,通信压力稍大些,但确保了仅仅传输接收一次。
先看协议中流程图,Client -> Server方向,会有一个总体印象:
Client | Message and direction | Server |
---|---|---|
QoS = 2 DUP = 0 Message ID = x Action: Store message | PUBLISH----------> | Action(a) Store message or Actions(b): Store message ID Publish message to subscribers |
PUBREC<---------- | Message ID = x | |
Message ID = x | PUBREL----------> | Actions(a): Publish message to subscribers Delete message or Action(b): Delete message ID |
Action: Discard message | PUBCOMP<---------- | Message ID = x |
Server | Message and direction | Subscriber |
---|---|---|
QoS = 2 DUP = 0 Message ID = x | PUBLISH----------> | Action: Store message |
PUBREC<---------- | Message ID = x | |
Message ID = x | PUBREL----------> | Actions: Make message available |
PUBCOMP<---------- | Message ID = x |
消息顺序
消息顺序会受许多因素的影响,但对于服务器程序,必须保证消息传递流程的每个阶段要和开始的顺序一致。例如,在QoS level 2定义的消息流中,PUBREL流必须和PUBLISH流具有相同的顺序发送:Client | Message and direction | Server |
---|---|---|
PUBLISH 1----------> PUBLISH 2 ----------> PUBLISH 3 ----------> | ||
PUBREC 1<---------- PUBREC 2 <---------- | ||
PUBREL 1----------> | ||
PUBREC 3<---------- | ||
PUBREL 2----------> | ||
PUBCOMP 1<---------- | ||
PUBREL 3----------> | ||
PUBCOMP 2<---------- PUBCOMP 3 <---------- |
在流动消息(in-flight)窗口1中,每个传递流在下一个流开始之前完成。这保证消息以提交的顺序传递
在流动消息(in-flight)大于1的窗口,只能在QoS level内被保证消息的顺序
消息的持久化
在MQTT协议中,PUBLISH消息固定头部RETAIN标记,只有为1才要求服务器需要持久保存此消息,除非新的PUBLISH覆盖。对于持久的、最新一条PUBLISH消息,服务器不但要发送给当前的订阅者,并且新的订阅者(new subscriber,同样需要订阅了此消息对应的Topic name)会马上得到推送。
Tip:新来乍到的订阅者,只会取出最新的一个RETAIN flag = 1的消息推送,不是所有。
消息流的编码/解码
MQTT协议中,由目前定义的14种类型消息在客户端和服务器端之间数据进行交互。若以JAVA语言构建MQTT服务器,可选择Netty作为基础。在Netty中,数据的进入和流出,代表了一次完整的交互。无论是要进入的还是要流出的数据(单独以服务器为例),都可看做字节流。若把每种类型消息抽象为一个具体对象,那么处理起来就不难了。
客户端->服务器,进入的字节流,逐个字节/单位读取,可还原成一个具体的消息对象(解码的过程)。
要发送到客户端的消息对象,转换(编码)成字节流,然后由TCP通道流转到接收者。
小结
断断续续记录了MQTT 3.1协议的若干阅读笔记,总之是把协议个人认为不够清晰,或者我不好理解的地方,着重进行了分析。也便于自己以后回过来头来翻阅,不是那么快的忘却。原文 http://www.blogjava.net/yongboy/archive/2014/02/15/409893.html
相关文章推荐
- qt4.7 opengl基础实例
- MQTT协议笔记之发布流程
- MQTT协议笔记之连接和心跳
- MQTT协议笔记之头部信息
- Qt5之于Qt4的发展
- QT下载地址
- QT5提示can not find -lGL的解决方法
- Qt应用程序打包问题之错误: 发现不同 CPU 类型的模块。
- Qt、QtCreator及opencv的安装
- Ubuntu QT5.5.0 安装
- ubuntu 下cmake、QT、geany的安装
- Tetris -- PyQT实现的俄罗斯方块
- Qt 图像处理之 灰度变换
- Qt 图像处理之 灰度变换
- VS2010+Qt5+OSG3.0开发环境搭建
- Qt程序部署时遇到的坑:could not find or load the Qt platform plugin "windows"
- Qt 延迟刷新View的技巧
- ARM的QT phonon 的移植
- Ubuntu 12.04安装qtcreator后无法打开,终端运行提示:Could not initialize GLX
- VS集成Qt环境搭建