前言
百度IoT的Broker设计我特别想参考的但是技术能力和时间不够去实现……网上只有一篇百度工程师的总结《共享行业的分布式MQTT设计》,这里将围绕这篇文章去讲解。
一、Broker集群架构
单机版MQTT Broker有连接数量和并发处理能力的限制,因此分布式必不可少。百度IoT采用的Akka Cluster来做集群管理,每个节点对等,不存在像Mosquitto这种用一台机器“桥接”做分布式产生的单点故障隐患。每个节点监听MemberUp、MemberDown、MemberUnreachable、ClusterMemberState等事件来感知其他节点的上下线,用Akka Actor实现节点间的消息通信。
二、Broker服务框架
百度Broker抽象了很多服务包括:
(1)Authentication Service、Authorization Service
MQTT的CONNECT阶段提供username和password,Broker可以用这些数据对客户端身份进行校验,我们称为验证(Authentication,AE);MQTT的PUBLISH、SUBSCRIBE阶段,需要对客户端订阅主题、发布主题进行权限控制,比如只能订阅含有自己DeviceID的主题,避免客户端订阅他人的主题窃听信息,我们称为鉴权(Authorization,AO)。
百度Broker提供用户名、密码的认证,以及每个客户端对哪些主题可读、可写。实现上,数据全保存在Mysql,通过内存或Redis做Cache加速,Cache回收策略为LRU。
百度这样的做法只适合于固定权限的控制,比如设备拥有的权限几乎相同,而且都是订阅格式相似的主题,只有其中的clientID不同而已,就可以做。如果有权限动态变化、设备粒度划分更细致的情况,采用Mysql+Redis就行不通了。
(2)Session Manager
MQTT定义了两种会话:持久会话(Persistent Session)、非持久会话(Transient Session)。持久会话在客户端断开重连后,之前的订阅数据、离线期间接收的消息依然存在;非持久会话断开连接就清空所有数据。对分布式Broker而言,如何实现持久会话就是一个难点。百度Broker的策略是,持久会话每个Broker都会同步一份,即使Broker宕机,其他Broker上也有相应的信息,以解决高可用问题;非持久会话放在内存里,只在连接的Broker上存在,连接断开或Broker崩溃后清空。
文中没有提到如何解决跨区问题(跨区时延高容易掉线,最好不做集群而是做数据同步,多个区域的Broker Session应该如何同步),以及Session每个节点都同步一份导致内存随设备数量线性增长的问题。
(3)Event Service
负责将每个Broker上发生的连接事件、断开连接事件、订阅事件、取消订阅事件通过Event Service发送给每个Broker,以达到同步的目的,类似于消息总线。实现上采用的Kafka,没有采用Akka通信的原因是这些事件需要持久化,比如Broker崩溃、网络波动后之前发送的未被消费的事件还存在。
文中没有提到订阅事件、取消订阅事件如何处理顺序消费的问题,因为订阅和取消订阅先后顺序会影响Session的同步,比如同一个主题,客户端取消订阅事件先于订阅事件被消费,会导致一直订阅着某个主题;相反订阅事件先于取消订阅事件,会导致订阅丢失。通过kafka的方式,如果用了重试策略保证可靠性,就可能导致这些问题。虽然客户端订阅后马上取消订阅这种情况几乎不存在,都是上线后订阅、下线前取消订阅。
(4)Session State Metadata Service
负责持久化Session元数据,它从Event Service接收数据,然后决定哪些数据需要持久化到Hbase存储,比如持久会话的订阅、取消订阅数据。
(5)Queue Service
管理和分配Queue。根据Session类型不同,分为持久队列(Persistent Queue)、非持久队列(Transient Queue),用于消息下发和离线消息存储。Persistent Queue基于Hbase实现,Transient Queue是内存实现。
(6)Quota Service
管理并发连接数、上行带宽、下行带宽限制。
(7)Metric Service
监控并发连接数、并发消息数、当前流量、服务运行指标(CPU、内存、网络吞吐)
三、连接层
百度的连接层编解码架构如下:
百度Broker连接层采用Net