天天看点

MQTT协议详解及v5.0实践

本文主要包含了以下内容:

1)MQTT协议演进历史及协议特点,总结和分析MQTT协议族的优缺点,分析和总结了为什么相比于其他协议,MQTT适合IoT,业内支持现状等。

2)阿里云IoT MQTT3和5协议在实践中的一些关键设计及思考。 包括连接复用、设备状态一致性、扩展增值能力设计等。

1. MQTT协议详解

1.1 MQTT协议演进

MQTT最初由IBM于20世纪90年代发明,最初是用于石油管道的传感器与卫星之间数据传输。MQTT v3.1.1于2014.10月正式发布,与此同时v3.1.1已成为OASIS协议标准(就是3.1.1已升级为国际物联网标准)。正如HTTP为人们通过web分享信息铺平了道路一样,MQTT标准化能将几十亿低成本、IoT设备连接到网络。毫无疑问,MQTT是当前最主流、增长最迅速的IoT应用层传输协议,目前,阿里云IoT平台许多在线设备都是通过MQTT接入的。

MQTT v5.0 于2018.5月正式发布,2019年3月,v5.0 成为了新的 OASIS 标准。v5.0在 v3.1.1的基础上做了较大的改变且不做向下兼容,显然是有太多的新东西要被引入,所有现有的实现要重新实现。 此次通过的v5.0是自2014年的v3.1.1以来最重要的协议升级,新协议能适应近年来行业发展的新需求,同时也为未来物联网行业发展的做了协议上的准备。 阿里云IoT平台从2020.1月开始支持 MQTT5.0。

1.2 MQTT协议族

每一种协议都是在特定上下文、约束下得出的折中设计,从来都没有完美的协议。 MQTT经过几十年的发展,针对不同场景已经演进出相对成熟的多版本协议。

1.2.1 MQTTv3

MQTTv3协议是为在低带宽、不可靠的网络上工作的传感器而设计的基于TCP的应用层协议,适用于IoT场景。 MQTT报文设计紧凑,可在严重受限的硬件设备和低带宽/高延迟的网络上实现稳定传输。它具有以下几项重要特性:使用发布/订阅消息模式,支持一对多的消息分发,解除设备和业务之间的耦合; 报文格式设计精简, 适用于小规模数据传输以及资源受限的IoT设备。 固定头部是2字节,开销很小,支持QoS0、QoS1、QoS2 三种消息QoS。

MQTT3.1.1在MQTTv3基础上引入了一些新特性, 主要包括:clientId优化,支持broker给设备指定clientId,增大了clientId最大长度。ack响应优化,connect ack中引入session Present标识,告诉设备当前broker有上次连接的会话信息,设备可以根据这个标记减少重复订阅等步骤。设备能够在等broker返回connect ack之前发布消息,这个特性有点类似tls的false start, 适用于对于突发模式的设备端。

MQTTv3/v3.1.1 在实际应用中存在以下不足:1)错误码设计的不够完整,设备难以完整感知到broker的处理异常;2)不支持设备跟broker之间的能力发现/协商,broker不能提供可选能力等。3)协议设计的过于精简,没有预留扩展空间,无法直接在协议层做扩展,协议能力相对简陋。4)对于一些高阶能力支持不够,例如协议层缺乏流控、优先级、报头压缩等功能。5)MQTT3是基于TCP的应用层协议,TCP固有的一些缺点也被MQTT继承了。

1.2.2 MQTT-SN

MQTT-SN(Sensor Networks) 是MQTT协议的传感器网络版本,最早用在zigBee无线网络中,主要面对电池供电,有限的处理器能力和存储能力的设备。只有很小的内存和CPU,TCP 对于这些设备来说非常奢侈,甚至无法允许TCP协议栈。 还有一些网络,比如 zigBee,报文的长度在几百字节以下,无法承载太大的数据包。MQTT-SN有主要特点:1) MQTT-SN支持运行在链路层、IP、UDP之上。2) QOS增加了-1级别,只用于传输,尽力而为,无保证。3)更丰富且开销更低的Topic类型。4)网络架构增加了SN网关。

1.2.3 MQTTv5

MQTT 5.0在协议层提供了更大的自定义扩展空间,平台基于扩展点可支持更丰富的协议能力。v3.1版本中,只能通过overlay方式,在业务层提供扩展能力。

MQTT5.0 主要特点如下:

设计目标 功能点 备注
提高错误反馈能力 Reason code& string on all ACKs ack有了错误码,端上处理可以更灵活。例如qos1如果被限流,端上可以根据code主动减缓速率
Server disconnect 端上根据断开原因,能采用更合理的重连退避策略(是否重连、重连间隔、redirect地址等)
增加可扩展能力 Payload format and content type 丰富消息格式,用户更友好
Message expiry 业务层也可以自己做,协议层原生支持更优雅
Subscription ID 客户端可以建立订阅标识符与消息处理程序的映射,提高客户端topic匹配速度
Will delay 类似疲劳度策略,一定时间内,设备如果重连成功,则不发遗嘱。
Server Keep Alive 服务端可以根据自身负载情况,动态调整max keepalive,实现动态keepalive
Assigned ClientID 支持云端给设备生成clientId
Server reference

1.跟http location类似

2.接入层支持切流和调度会更加灵活,不用再依赖DNS

User properties 给业务层提供极高的可扩展性
提高系统的伸缩性 Session expiry 便于快速重连场景下client对clean session的使用和管理
Shared Subscriptions 南向类似设备广播,北向类似服务端订阅
Optional Server feature availability 支持端-云能力协商
优化资源受限和小客户端接入 Subscription options 提供更细粒度的订阅策略,例如设备侧来决定是否允许自发自收
Topic Alias 类似hpack,减少带宽开销
Flow control 设备侧可配置的传输策略,可根据自身资源情况,灵活指定消息传输限制
Maximum Packet Size
No retry for QoS 1 and 2 messages 优化QoS1 消息重试策略
常见范式下沉至协议层 Request / Response user properties即可支持,broker不感知
Enhanced authentication 支持增强认证(例如支持Kerberos 认证、SCRAM认证)

1) 用户属性

设备上下行消息的时候支持携带用户自定义的属性,用户可以添加两端约定的属性数据,类似于MQ的标签,其中云端在转发应用消息时会保持所有用户属性的先后顺序。

2) 主题别名

设备上报消息的topic常常相对比较固定,数量不会特别多,那么只需首次发送消息的时候,通过携带Topic别名告知对端, 下次发送消息的时候,这个topic将会使用这个Topic别名的值来代替,即可减少上报Publish消息的报头。 通过将主题缩减为一个整型的数值可以达到减小MQTT报头大小的作用,从而减小传输流量开销,非常适用于带宽资源受限的设备。

3)请求/响应模式

MQTT3.1版本中通信双方需要事先协商好请求主题和响应topic。  MQTT5 新增了响应主题和相关数据,接收方只需关注怎么处理请求,而不用事先协商或考虑好怎么将响应正确返回给请求方。

4) 响应报文原因码和原因字符串

mqtt 3.1协议中原因码的种类较少,这些原因码所能表示的含义很少,并且mqtt3.1不支持服务端通过disconnect报文断开连接,导致设备端仅根据响应难以定位问题。 mqtt5.0有近50个原因码,绝大部分ack响应报文都包含了原因码,设备可以根据这些丰富的原因码定位请求是否成功以及问题。 MQTT5议能反馈更多的错误信息到端上,并且支持云端通过disconnect报文主动断开连接, 设备端知道服务端发生了什么,因而能更灵活的应对。

5) 服务端能力协商

支持告知设备端 云端所支持的功能列表,避免设备端使用云端不支持的功能。

1.3 业内现状

1.3.1 开源社区 & 云产商

MQTT协议详解及v5.0实践
  • 目前主流MQTT Broker开源社区基本已经支持v5.0,并且开源SDK也已经初步支持v5.0,与此同时,国内IoT云厂商还没有支持v5.0,但未来已来。

1.3.2 客户端SDK支持现状

MQTT协议详解及v5.0实践

2. MQTT协议层实践

2.1 MQTT应用架构

主要分为6大模块:

  • 基础接入模块:包括多版本协议编解码、多协议端口复用、会话管理、心跳检测、连接管理等。
  • 协议扩展模块:包括基于自定义协议扩展,实现的一系列扩展功能,包括通道解压缩、低功耗免ping等。
  • 增值消息服务:包括Rrpc、广播、时钟同步、脚本前置解析等。
  • 业务埋点模块:设备行为统计、在线时长聚合、网络延时诊断等。
  • 安全防御模块:包括黑名单机制、入口流控等。
  • 高可用模块:包括流量分组调度、容灾降级等。
  • 运维管控模块:主要包括流量分组调度、限流管理、连接诊断

2.2 协议层设计挑战

  • 设备状态一致性策略设计,包括session管理机制、心跳检测机制、异地登陆问题、状态最终一致性策略。
  • 在MQTT发布/订阅异步分发模型上,如何满足多样的业务场景。例如同步调用、广播等。
  • 如何同时满足不同场景下设备对MQTT接入需求,单应用上如何同时支持两个版本MQTT协议
  • MQTTv3协议过于精简,业务从MQTT3切换到v5的过渡时间,如何扩展协议层能力,提高客户接入体验。

2.3 MQTT关键策略设计

2.3.1 设备在线状态

协议层本地有session管理器来对本地会话进行管理,通过心跳检测、会话自检来保证跟设备之间的连接状态一致性,当前平台单设备不支持同时同设备多端登陆,基于分布式会话,协议层通过分布式会话识别异地登陆,将异常连接踢下线。

设备状态一致性策略:设备到MQTT协议接入层之间是tcp长连接,通过心跳机制保证心跳周期内设备状态的最终一致性。同时通过分布式会话版本号,保证分布式会话并发更新安全,通过上行消息/心跳定时触发会话自检机制,解决异常情况下本地/分布式会话状态不一致的问题。

2.3.2 消息推送模式

MQTT协议是基于PUB/SUB的异步通信模式,针对单设备纬度实现基础的发布/订阅推送外,还支持了复杂的消息推送方式:RRpc和在线广播。

在传统的基于PUB/SUB通信模式的中间件中, 消息的Producer/Consumer只负责生产和消费,彼此之间不会直接通讯。 而在某些业务场景不仅仅是将消息投递至订阅方,订阅方收到消息后可能还会执行一些操作并返回结果,PUB/SUB模式下实现这种请求/响应模式会非常繁琐,在MQTT中通信双方需要事先协商请求和响应topic。

针对这一痛点,协议层在发布订阅模式之上构建了一套Rpc通讯模式,解决开发者痛点。Rrpc模式允许Producer发出消息后,以同步形式等待Consumer消费这条消息并返回响应,达到类似Rpc的调用效果。Rrpc模式使得MQTT应用具备了同步调用的能力,扩展了使用场景,使其具备更多的可能性。

MQTT协议详解及v5.0实践
  • 通过topic中包含的messageId匹配请求与响应,对业务数据零侵入
  • messageId的生成与匹配、超时控制等逻辑,调用方无感知
  • 简化了业务方调用逻辑,扩展了MQTT使用场景。

2.3.3 多种类接入方式

MQTT协议层针对不同场景支持多种MQTT接入方式,同时支持tcp直连、tls、ws、wss等方式接入,用于满足不同场景接入需求。为了实现更好的网络穿透性,协议层实现了多协议端口复用,也就是一个端口同时支持多种协议。

MQTT协议详解及v5.0实践
  • 边解析边判断,处理效率高;
  • 节约常用端口,实现更好的网络穿透性
  • 内部能力扩展对设备侧无感知
  • 针对MQTT协议5和3,通过协议解析也实现了同时兼容。

2.3.4 自定义协议扩展

MQTTv3在实际应用中存在一些缺点,而MQTTv5生态的繁荣推广还需要很长时间的推进, 在MQTT3到5的过渡时间,我们在v3.1.1基础上通过overlay的方式,提供了扩展套件来解决客户痛点。

没有什么问题不是封一层解决不了的,如果有那就再封一层,思路:

  • 通过在建连clientId中扩展ext参数,实现端云之间能力协商
  • 通过扩展消息topic格式,实现支持自定义属性
  • 定义一套ext异常推送topic规范
环节 3.1协议扩展 5.0对标功能 带来的增值能力
建连 能力协商 Broker能力发现
  • 低功耗免心跳
  • 通道解压缩
  • 异常推送
传输 Topic扩展参数 用户属性
  • 脚本前置解析
  • 全链路traceid串联
离线
  • 异常离线原因推送
  • 动态注册错误推送
  • 丰富接入增值能力,提高接入体验
  • 后端能力提前ready沉淀,5.0升级可复用已有能力

2.4 未来展望

3. 参考资料

  1. ​​https://developer.ibm.com/zh/technologies/messaging/articles/iot-mqtt-why-good-for-iot/​​
  2. ​​http://docs.oasis-open.org/mqtt/mqtt/v5.0/cs02/mqtt-v5.0-cs02.html​​
  3. ​​http://modelbasedtesting.co.uk/2018/04/09/a-story-of-mqtt-5-0/​​
  4. ​​https://github.com/mqtt/mqtt.github.io/wiki/brokers​​

继续阅读