MQTT (Message Queuing Telemetry Transport,消息队列遥测传输) 是一种标准化的发布/订阅消息传输协议,设计于1999年,最初是为了在卫星之类的物体上使用。它是一个非常轻量级的协议,由于对带宽需求很低,从而成为了 M2M 通信或物联网应用的理想选择,现在已经成为这类场景最常见的协议之一。
本文会对该协议及一些使用范例做以简介,虽然没打算写成 MQTT 的综合性参考指南,但会提供足够的信息,让开发人员了解到如何安装运行这一协议。如果想要更深入地了解,可以参考 HiveMQ 所发布的系列文章。
发布/订阅
发布/订阅,通常也被成为 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 来提供加密连接。但这确实增加了发送与接收信息的计算复杂性,不但在约束系统中会造成问题,还会影响消息代理的性能。稍后我们会就这个问题进行更多讨论。
消息代理软件
有许多不同方式实现的可用消息代理,最常见的系统包括:
Mosquitto —— 这是最早在生产环境中可用的消息代理之一,以 C 语言编写,提供多种配置与高性能。
Mosca —— 以 Node.js 编写,可嵌入 Node 应用或以独立可执行文件的形式运行。由于配置简单并具有可扩展性,它也是我们最喜欢的消息代理,具有高性能的优点。
RSMB —— IBM 对 MQTT 协议的实现,也是最不常用的选项之一,不过它是一个用C语言编写的成熟系统。
HiveMQ —— HiveMQ 是一种相对较新的消息代理,面向企业环境,在博客上有很多关于 MQTT 不错的信息。
客户端库
几乎包含了所有流行语言的客户端库,想要具体了解的话,Paho 项目会是你的最佳选择。这个项目隶属于 Eclipse,旨在提供各种语言尽可能多样化的 MQTT 客户端实现参考。这是个很好的资源,包含以C、Java、Python、Javascript等语言编写的可用客户端。
结论
MQTT 是一个理想的协议,它在物联网与 M2M 通信中的应用是无限的。如果你需要轻量级的消息传输系统,那么它会是很好的选择,而且在未来几年中很可能会流行起来。希望本文能帮助读者对 MQTT 做以了解。