You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

89 lines
12 KiB
Markdown

2 years ago
# 26 | MQTT协议如何支持海量的在线IoT设备?
你好,我是李玥。
IoT也就是物联网一直是最近几年技术圈非常火的一个概念并且随着5G大规模商用IoT还将持续火下去。
那到底什么是物联网呢物联网这个词儿它的含义还不那么直观但你看它的英文IoT也就是Internet of Things的缩写Things这个单词我们知道它在英语里面几乎可以指代一切。翻译成中文我个人觉得“东西”这个词儿比较贴切。那物联网就可以理解为把所有东西都用互联网给连接起来。
这里面不仅仅包括像电脑、手机这样的智能设备还包括一些已经智能化的传统设备比如汽车、冰箱、路边的摄像头等等将来还将包括更多的、各种各样的物品比如水杯、衣服、工业用的各种设备和工具等等也就是所谓的万物互联。所以IoT它的未来绝对是大有可期的。
那这些物联网设备它要实现互相通信也必须有一套标准的通信协议MQTT就是专门为物联网设备设计的一套标准的消息队列通信协议。使用MQTT协议的IoT设备可以连接到任何支持MQTT协议的消息队列上进行通信。
这节课我们就一起来聊一聊MQTT协议以及如何把MQTT应用到生产系统中去。
## MQTT和其他消息队列的传输协议有什么不同
从宏观上来说MQTT和其他消息队列采用的传输协议是差不多的。它采用的也是“发布-订阅”的消息模型。网络结构上也是C/S架构IoT设备是客户端Broker是服务端客户端与Broker通信进行收发消息。
虽然MQTT和普通的消息队列相比在消息模型、功能和网络结构上都是差不多的但由于他们面对的使用场景是不一样的所以MQTT和普通的消息队列相比还是有很多区别的。我们看一下MQTT的使用场景有什么样的特点
首先它的客户端都是运行在IoT设备上。IoT设备它有什么特点最大的特点就是便宜一个水杯才卖几十块钱它上面的智能模块的成本十块钱最多了再贵就卖不出去了。十块钱的智能设备内存都是按照KB来计算的可能都没有CPU也不一定有操作系统整个设备就一个SoC。这样的设备就需要通信协议不能太复杂功能不能太多。另外IoT设备一般都采用无线连接很多设备都是经常移动的这就导致IoT设备的网络连接不稳定并且这种不稳定的网络是一个常态。
MQTT协议在设计上充分考虑了上面的这些特点。在协议的报文设计上极其的精简可以说是惜字如金。协议的功能也非常简单基本上就只有发布订阅主题和收发消息这两个最核心的功能。另外为了应对网络连接不稳定的问题MQTT增加了心跳和会话的机制。加入心跳机制可以让客户端和服务端双方都能随时掌握当前连接状态一旦发现连接中断可以尽快地重连。MQTT还加入了会话的机制在服务端来保存会话状态客户端重连之后就可以恢复之前的会话继续来收发消息。这样把复杂度转移到了服务端客户端的实现就会更简单。
MQTT面临的使用场景中另外一个很重要的特点就是服务端需要支撑海量的IoT设备同时在线。对于普通的消息队列集群服务的客户端都运行在性能强大的服务器上所以客户端的数量不会特别多。比如京东的JMQ集群日常在线的客户端数量大概是十万左右这样的规模就足够支撑全国人民在京东上买买买。这个规模已经是这个地球上少有的几个超大规模的消息队列集群之一了。
而MQTT的使用场景中需要支撑的客户端数量远不止几万几十万。比如北京交通委如果要把全市的车辆都接入进来这是就一个几百万客户端的规模。路边随处可见的摄像头每家每户都有的电视、冰箱每个人随身携带的各种穿戴设备这些设备的规模都是百万、千万级甚至是上亿的级别。
另外MQTT它是不支持点对点通信的一般的做法都是每个客户端都创建一个以自己ID为名字的主题然后客户端来订阅自己的专属主题用于接收专门发给这个客户端的消息。这就意味着在MQTT的集群中主题的数量是和客户端的数量基本是同一个量级的。
## 如何选择MQTT产品
如何能支持海量在线的IoT设备和海量的主题是每一个支持MQTT协议的消息队列面临的最大挑战。也是你在做MQTT服务端技术选型时需要重点考察的技术点。
目前开源的MQTT产品中有些是传统的消息队列通过官方或者非官方的扩展实现了MQTT协议的支持。也有一些专门的MQTT Server产品这些MQTT Server在协议支持层面大多数是没有问题的性能和稳定性方面也都可以满足要求。但是我还没有发现能很好支撑海量客户端和主题的开源产品。为什么呢
传统的消息队列虽然它可以通过扩展来支持MQTT协议但是它的整体架构在设计之初并没有考虑能支撑海量客户端和主题。比如之前我们讲过RocketMQ它的元数据是保存在NameServer的内存中Kafka是保存在ZooKeeper中这些存储都不擅长保存大量数据所以也支撑不了太多的客户端和主题。
另外一些开源的MQTT Server很多根本就没有集群功能或者集群功能做的不太完善。集群功能做的好的产品它们的开发者大多都把集群功能放到企业版中拿去卖钱了。
所以在做MQTT Server技术选型的时如果你接入IoT设备数量在十万以内是可以选择开源产品的选型的原则和选择普通消息队列是一样的我在《[02 | 该如何选择消息队列?](https://time.geekbang.org/column/article/109750)》这节课中讲过的选型原则都是适用的,优先选择一个流行的、你熟悉的开源产品就可以了。
如果说客户端的规模超过十万的量级需要支撑这么大规模的客户端数量服务端只有单个节点肯定是不够的必须用一个集群来支持并且这个集群是要能支持水平扩容的这些都是最基本的要求。这个时候就几乎没什么可供选择的开源产品了。这种情况建议选择一些云平台厂商提供的MQTT云服务价格相对比较低当然你可以选择价格更高的商业版MQTT Server。
另外一个选择就是基于已有的开源MQTT Server通过一些集成和开发来自行构建MQTT集群。接下来我跟你说一下构建一个支持海量客户端的MQTT集群应该如何来设计。
## MQTT集群如何支持海量在线的IoT设备
一般来说一个MQTT集群它的架构应该是这样的
![](https://static001.geekbang.org/resource/image/de/f1/deae9d8d95484a2d2499a47beeaebbf1.jpg)
这个图从左向右看首先接入的地址最好是一个域名这样域名的后面可以配置多个IP地址做负载均衡当然这个域名不是必需的。也可以直接连接负载均衡器。负载均衡可以选择像F5这种专用的负载均衡硬件也可以使用Nginx这样的软件只要是四层或者支持MQTT协议的七层负载均衡设备都可以选择。
负载均衡器的后面需要部署一个Proxy集群这个Proxy集群承担了三个重要的作用第一个作用是来承接海量IoT设备的连接第二个作用是来维护与客户端的会话第三个作用是作为代理在客户端和Broker之间进行消息转发。
在Proxy集群的后面是Broker集群负责保存和收发消息。
有的MQTT Server它的集群架构是这样的
![](https://static001.geekbang.org/resource/image/6a/ef/6a8fdaed5ba598a734691aa725e03bef.jpg)
它的架构中没有Proxy。实际上它只是把Proxy和Broker的功能集成到了一个进程中这两种架构它本质上没有太大的区别。所以这两种架构我们可以认为是同一种架构一起来分析。
前置Proxy的方式很容易解决海量连接的问题由于Proxy是可以水平扩展的只要用足够多数量的Proxy节点就可以抗住海量客户端同时连接。每个Proxy和每个Broker只用一个连接通信就可以了这样对于每个Broker来说它的连接数量最多不会超过Proxy节点的数量。
Proxy对于会话的处理方式可以借鉴Tomcat处理会话的方式。一种方式是将会话保存在Proxy本地每个Proxy节点都只维护连接到自己的这些客户端的会话。但是这种方式需要配合负载均衡来使用负载均衡设备需要支持sticky session保证将相同会话的连接总是转发到同一个Proxy节点上。另一种方式是将会话保存在一个外置的存储集群中比如一个Redis集群或者MySQL集群。这样Proxy就可以设计成完全无状态的对于负载均衡设备也没有特殊的要求。但这种方式要求外置存储集群具备存储千万级数据的能力同时具有很好的性能。
对于如何支持海量的主题比较可行的解决方案是在Proxy集群的后端部署多组Broker小集群比如说可以是多组Kafka小集群每个小集群只负责存储一部分主题。这样对于每个Broker小集群主题的数量就可以控制在可接受的范围内。由于消息是通过Proxy来进行转发的我们可以在Proxy中采用一些像一致性哈希等分片算法根据主题名称找到对应的Broker小集群。这样就解决了支持海量主题的问题。
## 总结
MQTT是专门为物联网设备设计的一套标准的通信协议。这套协议在消息模型和功能上与普通的消息队列协议是差不多的最大的区别在于应用场景不同。在物联网应用场景中IoT设备性能差网络连接不稳定。服务端面临的挑战主要是需要支撑海量的客户端和主题。
已有的开源的MQTT产品对于协议的支持都不错在客户端数量小于十万级别的情况下可以选择。对于海量客户端的场景服务端必须使用集群来支撑可以选择收费的云服务和企业版产品。也可以选择自行来构建MQTT集群。
自行构建集群最关键的技术点就是通过前置的Proxy集群来解决海量连接、会话管理和海量主题这三个问题。前置Proxy负责在Broker和客户端之间转发消息通过这种方式将海量客户端连接收敛为少量的Proxy与Broker之间的连接解决了海量客户端连接数的问题。维护会话的实现原理和Tomcat维护HTTP会话是一样的。对于海量主题可以在后端部署多组Broker小集群每个小集群分担一部分主题这样的方式来解决。
## 思考题
课后请你针对我们这节课讲到的Proxy做一个详细设计不用写文档只要画出如下三个UML图即可
Proxy的类图(UML Class Diagram):粒度要包含到下面两个时序图用到的主要的类和方法;
Proxy收发消息的时序图(UML Sequence Diagram)分别绘制出Proxy生产消息和消费消息这两个流程的时序图。
欢迎你将绘制好的设计图上传到GitHub上然后在评论区给出链接。在设计过程中如果你有任何问题也欢迎在评论区与我交流。
感谢阅读,如果你觉得这篇文章对你有一些启发,也欢迎把它分享给你的朋友。