Java后端八股笔记9 MQ

21 阅读6分钟

消息队列

使用一个队列来进行通信

  • 解耦:将两个系统解耦,即使其中一个系统挂了也是消息积压,不会
  • 异步:
  • 削峰:将短时间内的大量消息放入MQ,通过系统最大处理能力来处理。

RocketMQ

  • 阿里巴巴开源,完全使用Java开发
  • 功能齐全:延迟队列,消息事务等,kafka基本没有。
  • 性能较弱

基础概念

archifortopic-ef512066703a22865613ea9216c4c300.png

  • Topic(主题):消息传输和存储的顶层容器,用于标识一类业务逻辑的消息。Apache RocketMQ 的方案设计中,建议将不同业务类型的数据拆分到不同的主题中管理,通过主题实现存储的隔离性和身份识别与权限管理。

  • Queue(队列):消息存储和传输的实际容器

  • Message(消息):队列中传输的最小单元。生产者把数据包装成消息发送到服务端,服务端将消息给对应消费端进行消费。

  • Producer(生产者):发布消息的角色

  • Consumer(消费者):消息消费的角色。

    • 支持以推(push),拉(pull)两种模式对消息进行消费。
    • 支持集群方式和广播方式的消费。
    • 提供实时消息订阅机制,可以满足大多数用户的需求。
  • NameServer(名字服务器):一个简单的 Topic 路由注册中心,支持 Topic、Broker 的动态注册与发现。

    • Broker管理:接受 Broker 集群的注册信息并且保存,提供心跳检测机制
    • 路由信息管理:Producer 和 Consumer 通过 NameServer 知道整个 Broker 集群的路由信息,从而进行消息的投递和消费。
    • NameServer 通常会有多个实例部署,各实例间相互不进行信息通讯。Broker 是向每一台 NameServer 注册自己的路由信息,所以每一个 NameServer 实例上面都保存一份完整的路由信息。
  • Broker(代理服务器):负责消息的存储、投递和查询以及服务高可用保证。可采用主从架构。

部署模型:

p68921.png

  • 每个 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) {
    // 发送成功
}

消息事务机制

image-20250407142122992.png

  • 生产者产生消息,发送一条半事务消息到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可以处理该队列。

延迟队列

1720428795952-bba954e9-c9b6-45c5-aa05-8cc1d49c0e3c.png

broker 在接收到延时消息的时候,会将延时消息存入到延时Topic的队列中,然后ScheduleMessageService中,每个 queue 对应的定时任务会不停地被执行,检查 queue 中哪些消息已到设定时间,然后转发到消息的原始Topic,这些消息就会被各自的 producer 消费了。