一、为什么需要消息队列
1、哪些问题适合使用消息队列来解决?
异步处理:可以更快地返回结果,实现步骤之间的并发
(例如秒杀系统的的设计)
流量控制:使用消息队列隔离网关和后端服务,能根据下游的处理能力自动调节流量。缺点在于:增加了系统调用链环节,导致总体的响应时延变长
或者用一个有固定容量的消息队列加一个“令牌发生器”来实现实现一个令牌桶,从而限流
(例如如何避免过多的请求压垮秒杀系统)
服务解耦:实现系统应用之间的解耦
二、该如何选择消息队列
1、RabbitMQ:
相当轻量级,容易部署和使用
支持非常灵活的路由配置,在生产者(Producer)和队列(Queue)之间增加了一个 Exchange 模块
当大量消息积压的时候,会导致 RabbitMQ 的性能急剧下降
性能是主流消息队列中最差的
使用的编程语言非常小众,难以二次开发
2、RocketMQ
性能比 RabbitMQ 要高一个数量级,每秒钟大概能处理几十万条消息
国际上还没有那么流行,与周边生态系统的集成和兼容程度要略逊一筹
3、Kafka
异步批量的设计带来了很高的吞吐量,但是同步收发消息的响应时延比较高,因为当客户端发送一条消息的时候,Kafka 并不会立即发送出去,而是要等一会儿攒一批再发送。当你的业务场景中,每秒钟消息数量没有那么多的时候,Kafka 的时延反而会比较高
周边生态系统的集成和兼容程度很好
三、消息模型:主题和队列有什么区别
1、队列模型(旧)
如果有多个消费者接收同一个队列的消息,任何一条消息只能被其中的一个消费者收到
要求每个消费者都能收到全量的消息一个可行的解决方式是,为每个消费者创建一个单独的队列,让生产者发送多份。显然这是个比较蠢的做法
2、发布 - 订阅模型
RocketMQ 使用的消息模型是标准的发布 - 订阅模型。消息的发送方称为发布者,消息的接收方称为订阅者,服务端存放消息的容器称为**主题。**每份订阅中,订阅者都可以接收到主题的所有消息
为了确保消息的不丢失,RocketMQ 使用“请求 - 确认”机制。但是为了确保消息的有序性,在某一条消息被成功消费之前,下一条消息是不能被消费的。所以,每个主题在任意时刻,至多只能有一个消费者实例在进行消费,那就没法通过水平扩展消费者的数量来提升消费端总体的消费性能
为了解决这个问题,RocketMQ 在主题下面增加了队列的概念:每个主题包含多个队列,通过多个队列来实现多实例并行生产和消费。生产者将其消息发送给主题中的某个队列(根据一定的路由规则,比如取模之类的)。 消费者只保证在队列上保证消息的有序性,主题层面是无法保证的
订阅者的概念是通过消费组来体现的。每个消费组都消费主题中一份完整的消息,不同消费组之间消费进度彼此不受影响,一条消息被 Consumer Group1 消费过,也会再给 Consumer Group2 消费。消费组中包含多个消费者,同一个组内的消费者是竞争消费的关系,一条消息被消费者 Consumer1 消费了,那同组的其他消费者就不会再收到这条消息
由于消息需要被不同的组进行多次消费,所以消费完的消息并不会立即被删除,这就需要 RocketMQ 为每个消费组在每个队列上维护一个消费位置(Consumer Offset)
**Q:rocketmq,一个消费组在一个主题下的多个队列并发消费就无法保证消息的顺序性。这种该如何处理?
**
按照订单ID或者用户ID,用一致性哈希算法,计算出队列ID,指定队列ID发送,这样可以保证相同的订单/用户的消息总被发送到同一个队列上,就可以确保严格顺序了
Kafka 和RocketMQ结构相同,唯一的区别是在 Kafka 中,队列这个概念的名称不一样,Kafka 中对应的名称是“分区(Partition)”
四、如何利用事务消息实现分布式事务
1、什么是分布式事务
分布式事务,更多情况下,是在分布式系统中事务的不完整实现。比较常见的分布式事务实现有 2PC(Two-phase Commit,也叫二阶段提交)、TCC(Try-Confirm-Cancel) 和事务消息。每一种实现都有其特定的使用场景,也有各自的问题,都不是完美的解决方案
其中,事务消息适用的场景主要是那些需要异步更新数据,并且对数据实时性要求不太高的场景**(最终一致性)**
2、消息队列如何实现分布式事务
系统1在消息队列上开启一个事务,给消息服务器发送一个**“半消息”**(半消息和普通消息的唯一区别是,在事务提交之前,对于消费者来说,这个消息是不可见的。)
接着系统1执行本地事务。如果执行成功,那就提交事务消息,系统2就可以消费到这条消息;如果执行失败,那就回滚事务消息,系统2就不会收到这条消息。
如果在第四步提交事务消息时失败了怎么办?Kafka 直接抛出异常,让用户自行处理
RocketMQ 则给出了另外一种解决方案:增加了事务反查的机制来解决事务消息提交失败的问题。Broker (MQ Server)会定期去 Producer 上反查这个事务对应的本地事务的状态,然后根据反查结果决定提交或者回滚这个事务。我们的业务代码需要实现一个反查本地事务状态的接口,告知 RocketMQ 本地事务是成功还是失败。
使用 RocketMQ 事务消息功能实现分布式事务的流程如下图: