消息队列
使用一个队列来进行通信
- 解耦:将两个系统解耦,即使其中一个系统挂了也是消息积压,不会
- 异步:
- 削峰:将短时间内的大量消息放入MQ,通过系统最大处理能力来处理。
RocketMQ
- 阿里巴巴开源,完全使用Java开发
- 功能齐全:延迟队列,消息事务等,
kafka基本没有。 - 性能较弱
基础概念
-
Topic(主题):消息传输和存储的顶层容器,用于标识一类业务逻辑的消息。Apache RocketMQ 的方案设计中,建议将不同业务类型的数据拆分到不同的主题中管理,通过主题实现存储的隔离性和身份识别与权限管理。
-
Queue(队列):消息存储和传输的实际容器
-
Message(消息):队列中传输的最小单元。生产者把数据包装成消息发送到服务端,服务端将消息给对应消费端进行消费。
-
Producer(生产者):发布消息的角色
-
Consumer(消费者):消息消费的角色。
- 支持以推(push),拉(pull)两种模式对消息进行消费。
- 支持集群方式和广播方式的消费。
- 提供实时消息订阅机制,可以满足大多数用户的需求。
-
NameServer(名字服务器):一个简单的 Topic 路由注册中心,支持 Topic、Broker 的动态注册与发现。
- Broker管理:接受 Broker 集群的注册信息并且保存,提供心跳检测机制;
- 路由信息管理:Producer 和 Consumer 通过 NameServer 知道整个 Broker 集群的路由信息,从而进行消息的投递和消费。
- NameServer 通常会有多个实例部署,各实例间相互不进行信息通讯。Broker 是向每一台 NameServer 注册自己的路由信息,所以每一个 NameServer 实例上面都保存一份完整的路由信息。
-
Broker(代理服务器):负责消息的存储、投递和查询以及服务高可用保证。可采用主从架构。
部署模型:
- 每个 Broker 与 NameServer 集群中的所有节点建立长连接,定时注册 Topic 信息到所有 NameServer。
- Producer 与 NameServer 集群中的其中一个节点建立长连接,定期从 NameServer 获取 Topic 路由信息,并向提供 Topic 服务的 Master 建立长连接,且定时向 Master 发送心跳。Producer 完全无状态。
- Consumer 与 NameServer 集群中的其中一个节点建立长连接,定期从 NameServer 获取 Topic 路由信息,并向提供 Topic 服务的 Master、Slave 建立长连接,且定时向 Master、Slave 发送心跳。Consumer 既可以从 Master 订阅消息,也可以从 Slave 订阅消息。
消息积压
指生产端与消费端能力不一致,导致大量消息积压,一般为消费端能力过弱或出现bug导致宕机。
紧急扩容:
- 先修复bug,保证consumer正常运行,然后停掉所有的consumer。
- 新建一个topic,部署10倍的临时queue
- 新写一个consumer,将积压的消息进行消费,轮询均匀写入10个临时queue
- 部署10倍的临时consumer,对临时queue中的消息进行消费
- 全部消费完之后恢复原状
重复消费
为了防止消息丢失,生产端会重复发很多消息到消费端,导致重复消费。
解决思路是:保证消息的唯一性,即使多次传输,也不让消息的多次消费带来影响,也就是保证消息等幂性。
具体办法:在消息生产时,生成一全局唯一 id 作为去重和幂等的依据,避免同一条消息被重复消费
消息可靠性
生产者
可靠性保障(确保消息成功发送)
同步发送机制
阻塞等待Broker返回ACK
// 同步发送,等待Broker确认
SendResult sendResult = producer.send(message);
if (sendResult.getSendStatus() == SendStatus.SEND_OK) {
// 发送成功
}
消息事务机制
- 生产者产生消息,发送一条半事务消息到MQ服务器
- MQ收到消息后,将消息持久化到存储系统,这条消息的状态是待发送状态。
- MQ服务器返回ACK确认到生产者,此时MQ不会触发消息推送事件
- 生产者执行本地事务(与业务操作相关的数据库事务)
- 如果本地事务执行成功,即commit执行结果到MQ服务器;如果执行失败,发送rollback。
- 如果是正常的commit,MQ服务器更新消息状态为可发送;如果是rollback,即删除消息。
- 如果消息状态更新为可发送,则MQ服务器会push消息给消费者。消费者消费完就回ACK。
- 如果MQ服务器长时间没有收到生产者的commit或者rollback,它会反查生产者,然后根据查询到的结果执行最终状态。
Broker
可靠性保障(确保消息不丢失)
-
消息持久化
- 同步刷盘:消息写入后,立即刷到磁盘(最可靠)
- 异步刷盘:定期批量刷盘(性能高,极端情况可能丢失少量数据)
-
主从复制机制
- 同步复制:消息写入主节点后,同步复制到从节点才返回ACK
- 异步复制:主节点写入成功即返回,异步复制到从节点
- 多副本:支持多Master多Slave架构
-
故障转移
- NameServer发现机制:自动检测Broker存活状态
- 主从切换:Master故障时,Slave自动提升为Master
- 消息重投:消费失败时,消息会自动重投到其他Broker
消费者
可靠性保障(确保消息成功消费)
-
ACK确认机制
- CONSUME_SUCCESS:消费成功,消息被标记为已消费
- RECONSUME_LATER:消费失败,消息会重新投递
-
消费重试机制
- 16个重试级别:最多重试16次
- 延迟重试:失败后延迟一段时间再重试(避免雪崩)
- 死信队列:重试16次后仍失败,进入死信队列人工处理
messageDelayLevel = 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
消息顺序性
局部顺序一致,将需要保持顺序一致的消息放入同一个队列中。Broker通过加锁保证只有一个consumer可以处理该队列。
延迟队列
broker 在接收到延时消息的时候,会将延时消息存入到延时Topic的队列中,然后ScheduleMessageService中,每个 queue 对应的定时任务会不停地被执行,检查 queue 中哪些消息已到设定时间,然后转发到消息的原始Topic,这些消息就会被各自的 producer 消费了。