消息队列实战笔记(四) 进阶(3)

370 阅读6分钟

八、RocketMQ与Kafka的事务

RocketMQ 中的事务,它解决的问题是,确保执行本地事务和发消息这两个操作, 要么都成功,要么都失败。并且,RocketMQ 增加了一个事务反查的机制,来尽量提高事务执行的成功率和数据一致性。
而 Kafka 中的事务,它解决的问题是,确保在一个事务中发送的多条消息, 要么都成功,要么都失败。 注意,这里面的多条消息不一定要在同一个主题和分区中,可以是发往多个主题和分区的消息。当然,你可以在 Kafka 的事务执行过程中,加入本地事务,来实现和RocketMQ 中事务类似的效果,但是 Kafka 是没有事务反查机制的。

RocketMQ 事务

首先给待发送消息添加了一个属性PROPERTY_TRANSACTION_PREPARED,标明这是一个事务消息,也就是半消息,然后会像发送普通消息一样去把这条消息发送到 Broker 上。如果发送成功了,就开始执行本地事务的方法,最后,根据半消息发送的结果和本地事务执行的结果,来决定提交或者回滚事务。在实现方法 endTransaction() 中,producer 就是给 Broker 发送了一个单向的 RPC 请求,告知Broker 完成事务的提交或者回滚。由于有事务反查的机制来兜底,这个 RPC 请求即使失败或者丢失,也都不会影响事务最终的结果。最后构建事务消息的发送结果,并返回。

RocketMQ 并没有把半消息保存到消息中客户端指定的那个队列中,而是记录了原始的主题队列后,把这个半消息保存在了一个特殊的内部主题RMQ_SYS_TRANS_HALF_TOPIC 中,使用的队列号固定为 0。 这个主题和队列对消费者是不可见的,所以里面的消息永远不会被消费。这样,就保证了在事务提交成功之前,这个半消息对消费者来说是消费不到的。

RocketMQ 是如何进行事务反查的:在 Broker 的TransactionalMessageCheckService 服务中启动了一个定时器, 定时从半消息队列中读出所有待反查的半消息,针对每个需要反查的半消息,Broker 会给对应的 Producer 发一个要求执行事务状态反查的 RPC 请求。

最后,提交或者回滚事务实现的逻辑是差不多的,首先把半消息标记为已处理,如果是提交事务,那就把半消息从半消息队列中复制到这个消息真正的主题和队列中去,如果要回滚事务,这一步什么都不需要做,最后结束这个事务。

九、MQTT协议

MQTT 是专门为物联网设备设计的一套标准的消息队列通信协议。使用 MQTT 协议的 IoT 设备,可以连接到任何支持 MQTT 协议的消息队列上,进行通信。

MQTT 和其他消息队列的传输协议的异同

从宏观上来说,MQTT 和其他消息队列采用的传输协议是差不多的。它采用的也是“发布 -订阅”的消息模型。网络结构上,也是 C/S 架构,IoT 设备是客户端,Broker 是服务端,客户端与 Broker 通信进行收发消息。

IoT 设备的特点就是便宜, IoT 设备一般都采用无线连接,很多设备都是经常移动的,这就导致,IoT 设备的网络连接不稳定。
MQTT 在协议的报文设计上极其的精简。协议的功能也非常简单,基本上就只有发布订阅主题和收发消息这两个最核心的功能。另外,为了应对网络连接不稳定的问题,MQTT 增加了心跳和会话的机制。加入心跳机制可以让客户端和服务端双方都能随时掌握当前连接状态,一旦发现连接中断,可以尽快地重连。MQTT 还加入了会话的机制,在服务端来保存会话状态,客户端重连之后就可以恢复之前的会话,继续来收发消息。这样,把复杂度转移到了服务端,客户端的实现就会更简单。

服务端需要支撑海量的 IoT 设备同时在线。

MQTT 不支持点对点通信的。
一般的做法都是,每个客户端都创建一个以自己ID 为名字的主题,然后客户端来订阅自己的专属主题,用于接收专门发给这个客户端的消息。这就意味着,在 MQTT 的集群中,主题的数量是和客户端的数量基本是同一个量级的。

MQTT 集群如何支持海量在线的 IoT 设备?

前置 Proxy 的方式很容易解决海量连接的问题,由于 Proxy 是可以水平扩展的,只要用足够多数量的 Proxy 节点,就可以抗住海量客户端同时连接。每个 Proxy 和每个 Broker 只用一个连接通信就可以了,这样对于每个 Broker 来说,它的连接数量最多不会超过 Proxy节点的数量。

负载均衡器的后面,需要部署一个 Proxy 集群,这个 Proxy 集群承担了三个重要的作用:第一个作用是来承接海量 IoT 设备的连接,第二个作用是来维护与客户端的会话,第三个作用是作为代理,在客户端和 Broker 之间进行消息转发。

Proxy 对于会话的处理方式,可以借鉴 Tomcat 处理会话的方式。一种方式是,将会话保存在 Proxy 本地,每个 Proxy 节点都只维护连接到自己的这些客户端的会话。 但是,这种方式需要配合负载均衡来使用,负载均衡设备需要支持 sticky session,保证将相同会话的连接总是转发到同一个 Proxy 节点上。另一种方式是,将会话保存在一个外置的存储集群中,比如一个 Redis 集群或者 MySQL 集群。 这样 Proxy 就可以设计成完全无状态的,对于负载均衡设备也没有特殊的要求。但这种方式要求外置存储集群具备存储千万级数据的能力,同时具有很好的性能。

对于如何支持海量的主题,比较可行的解决方案是,在 Proxy 集群的后端,部署多组Broker 小集群,比如说,可以是多组 Kafka 小集群,每个小集群只负责存储一部分主题。 这样对于每个 Broker 小集群,主题的数量就可以控制在可接受的范围内。由于消息是通过 Proxy 来进行转发的,我们可以在 Proxy 中采用一些像一致性哈希等分片算法,根据主题名称找到对应的 Broker 小集群。这样就解决了支持海量主题的问题。

十、JMQ的Broker是如何异步处理消息的?