物联网发展这么多年,始终没有一个通用的协议,而MQTT的出现的似乎要打破这个僵局,那么问题来了:为什么是MQTT?而不是CoAP、不是AMQP、不是JMS、不是DDS、甚至不是HTTP呢?
MQTT是“Message Queuing Telemetry Transport”的英文缩写意思是“消息队列遥测传输”是IBM开发的一个即时通讯协议。它是一种轻量级的、基于代理的“发布/订阅”模式的消息传输协议。其具有协议简洁、小巧、可扩展性强、省流量、省电等优点,而且已经有PHP,JAVA,Python,C,C#等多个语言版本,基本可以使用在任何平台上,几乎可以把所有联网物品和外部连接起来,所以特别适合用来当做物联网的通信协议。MQTT有哪些特点?又是如何应用的呢?
MQTT协议的设计原则
由于物联网的环境是非常特别的,所以MQTT遵循以下设计原则:
精简,不添加可有可无的功能。
发布/订阅(Pub/Sub)模式,方便消息在传感器之间传递。
允许用户动态创建主题,零运维成本。
把传输量降到最低以提高传输效率。
把低带宽、高延迟、不稳定的网络等因素考虑在内。
支持连续的会话控制。
理解客户端计算能力可能很低。
提供服务质量管理。
假设数据不可知,不强求传输数据的类型与格式,保持灵活性。
关于MQTT的几个关键词
发布/订阅
发布/订阅,通常也被成为 pub-sub 模式是 MQTT 的核心,除了基于同一个消息代理的发布者和订阅者之外,还有一些其它节点围绕着该消息代理呈星型拓扑分布。这个模型与标准的客户端/服务器迥然不同,一开始看似有些奇怪,但它提供的去耦能力在很多情况下都有巨大的优势。
客户端可以发布或订阅特定的主题(topic,有些类似信息主题),根据使用它们的消息代理来决定谁会收到信息。MQTT 的主题有特定的语法,使用斜杠(/)作为分隔符,整体呈层次结构,非常类似 URL 中的路径格式,因此厨房中的温度传感器也许会发布到类似“sensors/temperature/home/kitchen” 这样的主题。
我们看一个例子:想象一下有一个网络,将全世界的温度传感器连接起来,提供气象服务。所有这些传感器保持与某个消息代理中间件相连接,每隔10分钟报告一次当前的温度。他们基于自身位置按照下面的格式向特定主题发布信息:
sensors/temperature/{country}/{city}/{street name}
那么在伦敦贝克街(Baker Street)的某个传感器就会向“sensors/temperature/uk/london/baker_street”发布一条包含当前温度的信息。
MQTT 示例拓扑
气象服务需要保证历史温度数据库的数据最新,因此创建了订阅到 MQTT主题的数据库服务,数据库服务会在收到最新温度信息时发出提示。不过这里存在一个问题:数据库服务需要了解到全世界所有的温度传感器,而将每个传感器订阅到独立的主题会非常复杂,幸好 MQTT 有相应的解决方案:通配符(wildcards)。
通配符
在 MQTT 中有两个可用的通配符,分别是+和#,+表示匹配单一层级中的任意主题,#表示匹配任意数量的层次。因此在全球温度数据库中可能会有订阅到 sensors/temperature/# 的服务,它能从全世界的任何一个传感器接收温度读数。但如果英国政府想要在自己的温度服务中利用这些数据,只要订阅到 sensors/temperature/uk/# ,就可以限制范围,只接受英国的传感器读数。如果某个服务想要接收某个特定位置所有类型的传感器数据,可以使用类似这样的格式:
sensors/+/uk/london/bakerstreet_
正如你所见,这是一个极优秀的模块化系统,添加新的传感器与数据库只是小事一桩。而且该系统在性能方面也很优秀,MQTT 消息代理可以高度并行化并采用事件驱动,从而使得单个消息代理可以轻易扩展到每秒处理数万条信息的级别。
服务质量(QoS)
MQTT 的设计初衷是为了在不可靠的网络中运作良好,为不同的场景提供了三个级别的服务质量,允许客户端指定自己想要的可靠性级别。
QoS Level 0:至多一次
这是最简单的级别,无需客户端确认,其可靠性与基础网络层 TCP/IP 一致。
QoS Level 1:至少一次,有可能重复
确保至少向客户端发送一次信息,不过也可发送多次;在接收数据包时,需要客户端返回确认消息(ACK 包)。这种方式常用于传递确保交付的信息,但开发人员必须确保其系统可以处理重复的数据包。
QoS Level 2:只有一次,确保消息只到达一次
这是最不常见的服务质量级别,确保消息发送且仅发送一次。这种方法需要交换4个数据包,同时也会降低消息代理的性能。由于相对比较复杂,在 MQTT 实现中通常会忽略这个级别,请确保在选择数据库或消息代理前检查这个问题。
在 MQTT 中的服务质量水平划分
临终遗嘱信息
该协议提供了检测方式,利用KeepAlive机制在客户端异常断开时发现问题。因此当客户端电量耗尽、崩溃或者网络断开时,消息代理会采取相应措施。
客户端会向任意点的消息代理发送“临终遗嘱”(LWT)信息,当消息代理检测到客户端离线(连接并未关闭),就会发送保存在特定主题上的 LWT 信息,让其它客户端知道该节点已经意外离线。
安全性
MQTT(及通常的物联网设备)的安全性是一个相当大的主题,之后我们会详加描述,不过在本文中仅涉及两个主要的安全性功能:身份验证与加密。
身份验证是通过在 MQTT 连接包中发送用户名与密码来实现,几乎所有消息代理与客户端在实现时都支持这一功能。但由于信息太容易被拦截,为了避免,应当尽可能地使用安全传输层协议(TLS)。
协议本身未提供加密功能,但由于 MQTT 是在 TCP 上层运行的,我们可以很容易地利用 TLS 来提供加密连接。但这确实增加了发送与接收信息的计算复杂性,不但在约束系统中会造成问题,还会影响消息代理的性能。稍后我们会就这个问题进行更多讨论。
消息类型
MQTT拥有14种不同的消息类型:
1、CONNECT:客户端连接到MQTT代理
2、CONNACK:连接确认
3、PUBLISH:新发布消息
4、PUBACK:新发布消息确认,是QoS 1给PUBLISH消息的回复
5、PUBREC:QoS 2消息流的第一部分,表示消息发布已记录
6、PUBREL:QoS 2消息流的第二部分,表示消息发布已释放
7、PUBCOMP:QoS 2消息流的第三部分,表示消息发布完成
8、SUBSCRIBE:客户端订阅某个主题
9、SUBACK:对于SUBSCRIBE消息的确认
10、UNSUBSCRIBE:客户端终止订阅的消息
11、UNSUBACK:对于UNSUBSCRIBE消息的确认
12、PINGREQ:心跳
13、PINGRESP:确认心跳
14、DISCONNECT:客户端终止连接前优雅地通知MQTT代理
MQTT“三低一高”的物联网适配性
转换成本低
MQTT是IBM于17年前提出并着手立项的针对物联网的通信协议,也是涵盖范围最广的协议。基于 TCPIP的出身,让 MQTT“意外”获得转换buff,攻城狮们几乎不需要改造目前广泛使用以太网链路,就可以直接部署实施。只冲这点,每个IoT都得感谢 IBM。
网络要求低
物联网不比移动互联网,联网设备形态各异、网络状况未必理想,容错率低。
如说:你的车在荒郊野岭面临随时被偷的可能,依赖4G甚至3G网络都不现实。设备离线、断线不是物联网之痛,而是物联网日常。如果依赖设备本身的计算和存储能力,那么物联网的设备会非常昂贵——离万物互联越来越远了好吗?!
针对这些状况,MQTT采取遗言机制巧妙应对。针对物联网,众多协议中只有MQTT完美照顾了嵌入式设备,最小的数据包仅有2个比特!
硬件要求低
设备接入网络后,依赖网络稳定或者设备的运算存储工作依然是移动互联网的打法。物联网的接入设备应当是轻量的、低成本的、可快速铺开形成大数据效应和服务的设备。一个MQTT的Client端程序经常只有几十k大小。这就意味着更低的能耗,和更稳定的接入。
安全性高
自从著名智能硬件Nest被暴数据泄露,小到普通消费者、大到PM,对物联网数据的安全性就质疑不断。其实这是一个完全用意识弥补操作的事儿。
MQTT的安全性并不是天生的属性,而是它本身的开源性和可塑性给予开发人员更多想象空间:VPN、TLS加密、Client Identifier提供、二进制字节传输、SSL加密等加密手段都可以完美支持。程序猿可以舒一口气了,只要意识上去了,安全就上去了,操作层面无须担心。
MQTT的未来发展
目前大量的物联网设备依然使用HTTP或者TCP协议通信,因为从互联网时代沉积下来的旧习。在物联网设备形态单一、硬件厂商本身资金和技术雄厚的情况下,采取这种落后的策略一点问题都没有。因为巨头厂商有充分的资金搭建庞大的基础硬件设备,维护庞大的工程师团队进行开发。即便要忍受HTTP轮询的方式,目前物联网设备的体量仍能勉强支撑。然而随着传感器、智能硬件等关键技术门槛的突破、以及物联网行业人才的聚集,更多小团队带着不俗的产品原型和商业蓝图出现,物联网厚积薄发的事实正在逐步显现。巨头的站队、小团队的青睐,以及渴求转型的传统企业都让MQTT稳定、易用的特性逐渐被重视。
在未来,MQTT协议适用的场景将非常广泛。从最传统的物联网到车联网、智能家居、智慧商场、工业4.0等等,都是适用于MQTT的应用场景,甚至包括很多新一代的即时通讯工具或者消息服务,比方说Facebook的所有移动应用都是采用MQTT来进行高效传输的。相信,随着MQTT协议在国内的广泛应用,更多的智能设备、智能装备、智能工厂等物联网方向的项目将接入云平台,MQTT协议的覆盖面将会更加广泛。对于未来MQTT的发展之路,让我们拭目以待!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。