从上一篇博客中我们知道两台服务器之间的通信可以采用消息队列的方式。使用这种方式的好处在于生产者和消费者之间完全实现了异步通信,生产者只需要生产消息到消息队列,然后任意一个消费者都可能在往后的某个时间消费这个消息,但是消息只可以被一个消费者消费。这也就间接出现了一个生产者只会对应一个消费者的场景,实现了一种点对点的通信模式。
上面的模式是一种很不错的设计方案,假如有一天负载突然增大,我们也可以通过不断增加生产者服务器和消费者服务器来应对。但是这种模式也存在一个问题,加入我们现在有一笔订单,生产者把订单加入消息队列,这时我们希望和订单相对应的多个消费者都做出相应的处理,只用点对点的模式就会比较麻烦,它也会限制我们后续业务的扩展。这时可以采用发布订阅的模式来处理,生产者依然发送消息到消息队列,消息队列会把消息发送给所有订阅了这个消息队列的消费者。我们在上一篇博客的例子中就采用 epoll 的方式来实现了订阅消息队列的这种方式。当所有的消费者都消费完了这条消息,这条消息才会从消息队列中被删除。
下面我们来介绍一个消息服务很成熟的例子rocketmq ,在 Apache RocketMQ 里有一个主题(Topic)的定义,官方文档中称它是消息传输和存储的顶层容器,用于标识同一类业务逻辑的消息。
同样还有一个消费者分组(ConsumerGroup)的概念也需要被提及,消费者组被定义为多个消费行为一致的消费者。下面我们举个例子来说明一下这个分组意义,假设下图中的两个组分别代表商品和顾客,当消息队列中有一笔订单过来的时候商品和顾客这两个组中的服务器应当各自只有一台会消费这个数据,但是这个商品和顾客这两个组都是订阅了这个消息的。也就是说通过这种机制来很好地结合了点对点通信和发布订阅模式的通信。
下面就是 RocketMQ 的架构实例,我们至少需要两台消息服务器,一台是 NameServer,它用来管理所有的 BrokerServer,当生产者起来的时候它会首先通过 NameServer 获取到 BrokerServer 的信息,然后就会和 BrokerServer 之间发送信息,同样地,消费者也会采用这样的方式。从下图中我们很容易发现不管是 NameServer 还是 BrokerServer,他们都是可以被用作集群的,原因也很简单,假如 NameServer 宕机了,那整个消息服务就瘫痪了,所以会采用多个服务器备份的方式来搭建集群。
值得一提的是在上面的图中没有画出来消费者订阅 BrokerServer 的信息不光可以从主服务器上订阅,也可以从被服务器上订阅,这样可以加大消费者处理的性能,当然满足这种订阅机制的前提是主备服务器之间的数据是保证具备了强一致性的。
我们从图 3 和图 4 中是可以看到一个 BrokerServer 里是有多个消息队列的,这样可以增加消息被并行消费的能力,因为假如只有一个队列的话就会存在互斥的信息,有些消费者组就需要等待。如果包拯一个消费者组有一个属于自己的消息队列,每个消费者都从自己订阅的队列里拿消息,这样会持续增加消息队列的性能。
下面我们讨论一下消息服务器的事务处理,在我之前的博客中总结过分布式事务常见的两阶段提交法,如果您有兴趣的话可以点击这里 查看。两阶段提交法作为分布式事务而言一个很大的弊端就是它会降低性能,而消息服务器基本都是为了应对高并发的场景而存在的,所以 RocketMQ 对消息服务的事务也进行了一些处理来满足这样的场景。
下图是对 RocketMQ 事务消息的一个总结,首先生产者将消息发送至Apache RocketMQ服务端,然后 brokerServer 收到消息之后会先把消息进行持久化在返回成功给生产者,对应下图中的1,2两步。这个时候生产者开始执行本地事务逻辑,生产者根据本地事务执行结果向服务端提交二次确认结果(Commit或是Rollback),服务端收到确认结果后处理逻辑如下:
- Commit:将消息标记为可投递,并投递给消费者。
- Rollback:回滚事务。
第五步如果服务端未收到发送者提交的二次确认结果,或服务端收到的二次确认结果为Unknown未知状态,经过固定时间后,服务端将对消息生产者即生产者集群中任一生产者实例发起消息回查。生产者收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
最后我们总结一下消息服务器的作用:
- 可以用来进行模块间解耦:模块之间只需要发消息,不需要去调用对方的API;
- 可以具备队列所具有的FIFO的特点;
- 可以做到“削峰填谷”,起到一个缓冲的作用。
参考资料:
- 中国大学慕课JavaEE之Spring框架:https://www.icourse163.org/learn/XMU-1462056168
- rocketmq官网:https://rocketmq.apache.org/
Last modified on 2023-09-16