MQTT 5.0新特性
目前支持和使用最广泛的版本是MQTT 3.1.1版本。2017年8月,OASIS MQTT Technical Committee正式发布了用于Public Review的MQTT 5.0的草案。2018年,MQTT 5.0已正式发布,但是目前支持MQTT 5.0的Broker和Client库还比较有限。
MQTT 5.0在MQTT 3.1.1的基础上做了很多改变,并不是向下兼容的。在协议上,就增加了Property字段,也正是因为这个字段,使得MQTT 5.0可以支持众多的新特性。下面将从以下几个新特性进行介绍,这些新特性能够解决在3.1.1版本中较难处理的问题。
用户属性(User Properties)
5.0中可以在PUBLISH、CONNECT和带有Return Code的数据包中夹带一个和多个用户属性数据:
- 在PUBLISH包中携带的用户属性由发送方的应用定义,随消息被Broker转发到消息的订阅方;
- CONNECT和ACKs消息里面也可以带发送者自定义的用户属性数据。
在实际的项目中,除了关心收到的消息内容,往往也想知道这个消息来自于谁。例如:ClientA收到ClientB发布的消息后,ClientA想给ClientB发送一个回复,这时ClientA必须知道ClientB订阅的主题才能将消息传递给ClientB。在MQTT 3.1.1中,我们通常是在消息数据中包含发布方的信息,比如它订阅的主题等。5.0以后就可以把这些信息放在User Properties里面了。
共享订阅(Shared Subscriptions)
在3.1.1和之前的版本,订阅同一主题的订阅者都会同样收到来自这个主题的所有消息。例如你需要处理一个传感器的数据,假设这个传感器上传的数据量非常大且频率很高,你没有办法通过启动多个Client来分担处理的工作,因为多个Client都会同样收到所有消息。
针对上述的问题,也许可以启动一个Client来接收传感器的数据,并将这些数据分配给后面多个Worker来处理。这个用于接收数据的Client就是系统的瓶颈和单点故障之一。也可以通过主题分片,如让传感器依次发布到/topic1…/topicN来变通地解决这个问题,但仅仅是解决了部分问题,同时也提高了系统的复杂度。
而在5.0里面,MQTT可以实现Producer/Consumer模式了。多个Client(Consumer)可以一起订阅一个共享主题(Producer),来自这个主题的消息会依次均衡地发布给这些Client,实现订阅者的负载均衡,最终解决了这个问题。
这个功能在传统的队列系统中,比如RabbitMQ里面很常见。EMQTT在3.1.1上已经支持这个功能了,详情可以查询官方文档
消息过期(Publication Expiry Interval)
假设一个基于MQTT的共享单车平台,用户通过平台下发一条开锁指令给一辆单车,但是不巧的是,单车的网络信号(比如GSM)这时恰好断了,用户摇了摇头走开去找其他车了。过了两个小时以后,单车的网络恢复了,它收到了两个小时以前的开锁指令,该怎么做?
为了处理这种情况,在3.1.1和之前的版本,我们往往都是在消息数据里带一个消息过期时间,在接收端来判断消息是否过期。但是这要求设备端的时间和Server端保持一致,对于一些电量不是很充足的设备,一但断电,之后再启动时间就会变得不准确,会导致异常的出现。
5.0中终于包含了消息过期的功能,在发布的时候可以指定这个消息在多久之后过期,Broker不会将已过期的离线消息发送到Client。
重复主题
在MQTT 3.1.1和之前的版本里,PUBLISH数据包每次都需要带上发布的主题名,即使你每次发布的都是同一个主题。
在5.0里面,如果你将一条PUBLISH的主题名设为长度为0的字符串,那么Broker会使用你上一次发布的主题。这样降低了多次发布到同一主题(往往都是这样)的额外开销,对网络资源和处理资源都有限的系统非常有用。
5. Broker能力查询
在5.0里面,CONNACK包含了一些预定义的头部数据,用于标识Broker支持哪些MQTT协议功能。Client在连接之后就可以知道Broker是否支持自己所要用到的功能,这对于一些通用的MQTT设备生产商或者Client库的开发者很有用。
Pre-defined Header数据类型描述Retain AvailableBoolean是否支持Retained消息Maximum QoSNumberClient可以用于订阅和发布的最大QoSWildcard availableBoolean订阅时是否可以使用通配符主题Subscription identifiers availableBoolean是否支持Subscription Identifier(5.0特性)Shared Subscriptions availableBoolean是否支持共享订阅Maximum Message SizeNumber可发送的最大消息长度Server Keep AliveNumberBroker支持的最大Keep Alive值
双向DISCONNECT
在3.1.1和之前的版本里,只有Client在主动断开时会向Broker发送DISCONNECT包。如果因为某种错误Broker要断开和Client的连接,它只能直接断开底层TCP连接,Client则不会知道自己连接断开的原因,也无法解决错误,只是简单地重新连接、被断开、重新连接…
在5.0里面,Broker在主动断开和Client的连接时也会发送DISCONNECT包。同时,从Client到Broker,以及从Broker到Client的DISCONNECT包里面都会包含一个Reason Code,来标识断开的原因。
Reason code发送方描述0Client或Broker正常断开连接,不发送遗愿消息4Client正常断开,但是要求Broker发送遗愿消息129Client或BrokerMQTT数据包格式错误135Broker请求未授权143Broker主题过滤器格式正确,但是Broker不接收144Client或Broker主题名格式正确,但是Client或者Broker不接收153Client或者Broker消息体格式不正确154Broker不支持Retained消息155BrokerQoS等级不支持158Broker不支持共享订阅162Broker订阅时不支持通配符主题名