文章

《消息中间件知识个人笔记》

《消息中间件知识个人笔记》

一、消息中间件概述

1. 什么是消息中间件

消息中间件(Message Queue,简称 MQ)是一种应用程序对应用程序的通信方法。

消息中间件是在消息的传输过程中保存消息的容器,它提供了一套完整的消息生产、传递、消费的模式。


2. 消息中间件的作用

(1)应用解耦

场景:

  • 系统 A 通过接口调用发送数据到 B、C、D 三个系统
  • 如果 E 系统也要这个数据,那么系统 A 还要修改代码
  • 假如 B 系统现在不需要了,系统 A 也要修改代码

使用 MQ 后:

  • 系统 A 只需要把数据发送到 MQ
  • 其他系统如果需要数据,则从 MQ 中获取即可
  • 系统 A 不需要考虑给谁发送数据,不需要维护这个代码
  • 也不需要考虑其他系统是否调用成功、失败超时等情况

优点:

  • ✅ 降低系统间的耦合度
  • ✅ 提高系统的可扩展性和可维护性

(2)异步提速

场景:

  • 一个客户端请求发送进来,系统 A 会调用系统 B、C、D 三个系统
  • 同步调用的时间就是系统 A、B、C、D 的总和
  • 如果使用 MQ,系统 A 发送数据到 MQ,然后就可以返回响应给客户端
  • 不需要再等待系统 B、C、D 的响应,可以大大地提高性能

优点:

  • ✅ 提升用户体验和系统吞吐量
  • ✅ 将非核心业务异步化处理

(3)削峰填谷

场景:

  • 在高并发的场景下,如果短时间有大量的请求到达会压垮服务器
  • 使用了 MQ 之后,我们可以设置消息的消费速率
  • 这样就能够保护系统不被大量请求压垮

优点:

  • ✅ 保护系统稳定性
  • ✅ 平滑处理突发流量

(4)消息通讯

场景:

  • 消息队列一般都内置了高效的通信机制
  • 因此也可以用在纯的消息通讯场景
  • 比如实现点对点消息队列,或者聊天室等

优点:

  • ✅ 提供可靠的消息传递机制
  • ✅ 支持多种消息模式

3. 消息中间件的缺点

(1)系统可用性降低

  • ⚠️ 系统引入的外部依赖越多,系统稳定性越差
  • 一旦 MQ 宕机,就会对业务造成影响

解决方案:如何保证 MQ 的高可用?


(2)系统复杂度提高

  • ⚠️ MQ 的加入大大增加了系统的复杂度
  • 以前系统间是同步的远程调用,现在是通过 MQ 进行异步调用

需要考虑的问题:

  • 如何保证消息没有被重复消费?
  • 怎么处理消息丢失情况?
  • 如何保证消息传递的顺序性?

(3)一致性问题

  • ⚠️ A 系统处理完业务,通过 MQ 给 B、C、D 三个系统发消息
  • 如果 B 系统、C 系统处理成功,D 系统处理失败
  • 如何保证消息数据处理的一致性?

二、主流消息中间件对比

RabbitMQ vs RocketMQ vs Kafka

特性RabbitMQRocketMQKafka
开发语言ErlangJavaScala/Java
单机吞吐量万级(较低)10 万级(高)100 万级(极高)
时效性微秒级毫秒级毫秒级以内
可用性高(主从架构)非常高(分布式架构)非常高(分布式架构)
消息可靠性基本不丢失经过参数优化配置
可以做到 0 丢失
经过参数优化配置
可以做到 0 丢失
功能特性基于 Erlang 开发
并发能力强
性能极好
延时很低
管理界面丰富
MQ 功能较为完善
分布式架构
扩展性好
只支持主要的 MQ 功能
主要应用于大数据领域
在日志采集、实时计算等场景
被大规模使用
优点• 轻量级
• 易部署
• 支持多种协议
• 管理界面友好
• 高吞吐量
• 高可用性
• 分布式架构
• 功能完善
• 超高吞吐量
• 高性能
• 分布式架构
• 生态丰富
缺点• 吞吐量较低
• Erlang 语言不易定制
• 集群动态扩展麻烦
• 社区活跃度一般
• 客户端支持语言不多
• 功能相对简单
• 依赖 ZooKeeper
• 不支持消息查询
适用场景中小型公司
业务量不大
对性能要求不高
大型公司
业务量大
对可靠性要求高
大数据领域
日志采集
实时计算

选型建议

1. 选择 RabbitMQ

适用场景:

  • ✅ 中小型软件公司
  • ✅ 业务量不会太大
  • ✅ 对消息可靠性要求较高
  • ✅ 需要快速上手

理由:

  • 管理界面友好,易于上手
  • 社区活跃,文档丰富
  • 功能完善,支持多种协议

2. 选择 RocketMQ

适用场景:

  • ✅ 大型软件公司
  • ✅ 业务量大
  • ✅ 对可靠性要求极高
  • ✅ 需要定制化开发

理由:

  • 阿里巴巴开源,经过双十一考验
  • 高吞吐量,高可用性
  • Java 语言开发,易于定制
  • 功能完善,支持事务消息

3. 选择 Kafka

适用场景:

  • ✅ 大数据领域
  • ✅ 日志采集
  • ✅ 实时计算
  • ✅ 对吞吐量要求极高

理由:

  • 超高吞吐量
  • 分布式架构,易于扩展
  • 生态丰富,与大数据组件集成好

三、RocketMQ 详解

1. RocketMQ 核心概念

(1)Producer(生产者)

定义:负责生产消息,一般由业务系统负责生产消息。

特点:

  • 一个消息生产者会把业务应用系统里产生的消息发送到 Broker 服务器
  • RocketMQ 提供多种发送方式:同步发送、异步发送、顺序发送、单向发送

(2)Consumer(消费者)

定义:负责消费消息,一般是后台系统负责异步消费。

特点:

  • 一个消息消费者会从 Broker 服务器拉取消息
  • 并将其提供给应用程序
  • 从用户应用的角度提供了两种消费形式:拉取式消费、推送式消费

(3)Broker(消息代理)

定义:消息中转角色,负责存储消息、转发消息。

特点:

  • Broker 服务器在 RocketMQ 系统中负责接收从生产者发送来的消息
  • 并存储、为消费者拉取消息做准备
  • Broker 也存储消息相关的元数据
  • 包括消费者组、消费进度偏移和主题和队列消息等

(4)NameServer(名称服务)

定义:名称服务充当路由消息的提供者。

特点:

  • 生产者或消费者能够通过名称服务查找各主题相应的 Broker IP 列表
  • 多个 NameServer 实例组成集群,但相互独立,没有信息交换

(5)Topic(主题)

定义:表示一类消息的集合,每个主题包含若干条消息。

特点:

  • 每条消息只能属于一个主题
  • 是 RocketMQ 进行消息订阅的基本单位

(6)Message Queue(消息队列)

定义:用于存储消息的物理地址。

特点:

  • 一个 Topic 中可以包含多个 Queue
  • 每个 Queue 中存放的就是该 Topic 的消息
  • 一个 Topic 的 Queue 也被称为一个 Topic 中消息的分区(Partition)

(7)Message(消息)

定义:消息系统所传输信息的物理载体。

特点:

  • 生产和消费数据的最小单位
  • 每条消息必须属于一个主题

(8)Tag(标签)

定义:为消息设置的标志,用于同一主题下区分不同类型的消息。

特点:

  • 来自同一业务单元的消息,可以根据不同业务目的在同一主题下设置不同标签
  • 标签能够有效地保持代码的清晰度和连贯性
  • 并优化 RocketMQ 提供的查询系统

2. RocketMQ 架构设计

架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
┌─────────────┐     ┌─────────────┐
│  Producer   │     │  Producer   │
│   Group     │     │   Group     │
└──────┬──────┘     └──────┬──────┘
       │                   │
       │    ┌──────────────┤
       │    │              │
       ▼    ▼              ▼
┌─────────────────────────────┐
│       NameServer Cluster    │
│  ┌────────┐    ┌────────┐  │
│  │  NS1   │    │  NS2   │  │
│  └────────┘    └────────┘  │
└─────────────────────────────┘
       │                   │
       │    ┌──────────────┤
       │    │              │
       ▼    ▼              ▼
┌─────────────────────────────┐
│       Broker Cluster        │
│  ┌────────┐    ┌────────┐  │
│  │ Master │    │ Master │  │
│  │ Broker │    │ Broker │  │
│  └────┬───┘    └────┬───┘  │
│       │             │       │
│  ┌────▼───┐    ┌───▼────┐  │
│  │ Slave  │    │ Slave  │  │
│  │ Broker │    │ Broker │  │
│  └────────┘    └────────┘  │
└─────────────────────────────┘
       │                   │
       │    ┌──────────────┤
       │    │              │
       ▼    ▼              ▼
┌─────────────┐     ┌─────────────┐
│  Consumer   │     │  Consumer   │
│   Group     │     │   Group     │
└─────────────┘     └─────────────┘

架构特点

1. NameServer 集群

  • 各实例间相互独立,没有信息交换
  • Broker 向所有 NameServer 注册路由信息
  • Producer/Consumer 可以从任意 NameServer 获取路由信息

2. Broker 集群

  • Master 负责读写
  • Slave 负责读和备份
  • 支持多 Master 多 Slave 模式
  • 每个 Broker 与 NameServer 集群中的所有节点建立长连接

3. Producer 集群

  • 与 NameServer 集群中的其中一个节点建立长连接
  • 定期从 NameServer 获取 Topic 路由信息
  • 向提供 Topic 服务的 Master 建立长连接

4. Consumer 集群

  • 与 NameServer 集群中的其中一个节点建立长连接
  • 定期从 NameServer 获取 Topic 路由信息
  • 向提供 Topic 服务的 Master、Slave 建立长连接

3. RocketMQ 工作流程

(1)启动 NameServer

  • NameServer 启动后监听端口
  • 等待 Broker、Producer、Consumer 连接
  • 相当于一个路由控制中心

(2)Broker 启动

  • 启动后与所有的 NameServer 保持长连接
  • 定时发送心跳包(包含当前 Broker 信息以及存储所有 Topic 信息)
  • 心跳包每隔 30 秒向所有 NameServer 发送一次

(3)创建 Topic

  • 创建 Topic 时需要指定该 Topic 要存储在哪些 Broker 上
  • 也可以在发送消息时自动创建 Topic

(4)Producer 发送消息

步骤:

  1. Producer 启动时先跟 NameServer 集群中的其中一台建立长连接
  2. 并从 NameServer 中获取当前发送的 Topic 存在哪些 Broker 上
  3. 轮询从队列列表中选择一个队列
  4. 然后与队列所在的 Broker 建立长连接从而向 Broker 发消息

(5)Consumer 消费消息

步骤:

  1. Consumer 启动时先跟 NameServer 集群中的其中一台建立长连接
  2. 并从 NameServer 中获取当前订阅 Topic 存在哪些 Broker 上
  3. 然后直接跟 Broker 建立连接通道
  4. 开始消费消息

4. RocketMQ 消息发送方式

(1)同步发送(Sync)

特点:

  • 发送者向 MQ 执行发送消息 API 时
  • 同步等待,直到消息服务器返回发送结果

适用场景:

  • ✅ 重要通知消息
  • ✅ 短信通知
  • ✅ 营销短信等

(2)异步发送(Async)

特点:

  • 发送者向 MQ 执行发送消息 API 时
  • 指定消息发送成功后的回调函数
  • 调用 API 后立即返回
  • 消息发送者线程不阻塞,直到运行结束
  • 消息发送成功或失败的回调任务在一个新的线程中执行

适用场景:

  • ✅ 对响应时间敏感的业务场景

(3)单向发送(Oneway)

特点:

  • 发送者向 MQ 执行发送消息 API 时
  • 直接返回,不等待消息服务器的结果
  • 也不注册回调函数
  • 即只发送请求不等待应答

适用场景:

  • ✅ 某些耗时非常短,但对可靠性要求并不高的场景
  • ✅ 例如日志收集

5. RocketMQ 消息消费模式

(1)集群消费(Clustering)

定义:相同 Consumer Group 的每个 Consumer 实例平均分摊消息。

特点:

  • 每条消息只需要被处理一次
  • Broker 中可以保证每条消息只被消费一次
  • 但是无法保证每次失败重投的消息路由到同一台机器上

适用场景:

  • ✅ 消费端集群化部署
  • ✅ 每条消息只需要被处理一次的场景

(2)广播消费(Broadcasting)

定义:相同 Consumer Group 的每个 Consumer 实例都接收全量的消息。

特点:

  • 一条消息被多个 Consumer 消费
  • 即使这些 Consumer 属于同一个 Consumer Group
  • 消息也会被 Consumer Group 中的每个 Consumer 都消费一次

适用场景:

  • ✅ 需要将消息广播到所有消费者的场景
  • ✅ 例如更新本地缓存

6. RocketMQ 消息存储

(1)CommitLog

定义:消息主体以及元数据的存储主体。

特点:

  • 存储 Producer 端写入的消息主体内容
  • 消息内容不是定长的
  • 单个文件大小默认 1G
  • 文件名长度为 20 位,左边补零,剩余为起始偏移量
  • 例如:00000000000000000000 代表了第一个文件,起始偏移量为 0
  • 文件大小为 1G = 1073741824
  • 当第一个文件写满了,第二个文件为 00000000001073741824

(2)ConsumeQueue

定义:消息消费队列,引入的目的主要是提高消息消费的性能。

特点:

  • RocketMQ 是基于主题 Topic 的订阅模式
  • 消息消费是针对主题进行的
  • 如果要遍历 CommitLog 文件,根据 Topic 检索消息是非常低效的
  • Consumer 可根据 ConsumeQueue 来查找待消费的消息

存储结构:

  • ConsumeQueue 作为消费消息的索引
  • 保存了指定 Topic 下的队列消息在 CommitLog 中的起始物理偏移量 offset
  • 消息大小 size 和消息 Tag 的 HashCode 值

(3)IndexFile

定义:索引文件,提供了一种可以通过 key 或时间区间来查询消息的方法。

特点:

  • Index 文件的存储位置是:$HOME/store/index/${fileName}
  • 文件名 fileName 是以创建时的时间戳命名的
  • 固定的单个 IndexFile 文件大小约为 400M
  • 一个 IndexFile 可以保存 2000W 个索引

7. RocketMQ 高可用机制

(1)NameServer 高可用

机制:

  • NameServer 之间互不通信
  • 单台 NameServer 挂掉,不影响其他 NameServer
  • 单台 NameServer 挂掉,不影响 Broker
  • 单台 NameServer 挂掉,不影响 Producer/Consumer

特点:

  • ✅ 无状态,易于扩展
  • ✅ 任意时刻可以增加或减少 NameServer 节点

(2)Broker 高可用

主从复制:

RocketMQ 的高可用机制是通过 Master 和 Slave 的配合达到的。

复制方式:

方式说明优缺点
同步复制Master 和 Slave 均写成功
才返回成功
• 优点:数据安全性高
• 缺点:性能较低
异步复制Master 写成功即返回成功• 优点:性能高
• 缺点:可能丢失消息

部署模式:

① 单 Master 模式

  • ⚠️ 风险较大,一旦 Broker 重启或宕机,会导致整个服务不可用
  • ❌ 不建议线上环境使用

② 多 Master 模式

  • ✅ 无 Slave,全是 Master
  • ✅ 配置简单,性能最高
  • ⚠️ 单台机器宕机期间,该机器上未被消费的消息在机器恢复之前不可订阅

③ 多 Master 多 Slave 模式(异步复制)

  • ✅ 每个 Master 配置一个 Slave
  • ✅ 有多对 Master-Slave
  • ✅ 主备有短暂消息延迟(毫秒级)
  • ⚠️ Master 宕机,消费者仍然可以从 Slave 消费,但可能丢失少量消息

④ 多 Master 多 Slave 模式(同步双写)

  • ✅ 每个 Master 配置一个 Slave
  • ✅ 有多对 Master-Slave
  • ✅ 主备都写成功,向应用返回成功
  • ✅ 数据与服务都无单点故障
  • ⚠️ 性能比异步复制模式略低

8. RocketMQ 消息可靠性保证

(1)Producer 端

发送确认机制:

  • 同步发送:等待 Broker 返回确认
  • 异步发送:通过回调函数确认
  • 失败重试机制

最佳实践:

  • ✅ 使用同步发送或异步发送
  • ✅ 配置合理的重试次数
  • ✅ 记录发送失败的消息

(2)Broker 端

持久化机制:

  • 消息写入 CommitLog
  • 支持同步刷盘和异步刷盘

主从复制:

  • 同步复制:Master 和 Slave 都写成功
  • 异步复制:Master 写成功即可

最佳实践:

  • ✅ 配置主从复制
  • ✅ 重要消息使用同步刷盘
  • ✅ 定期备份数据

(3)Consumer 端

消费确认机制:

  • 消费成功返回 CONSUME_SUCCESS
  • 消费失败返回 RECONSUME_LATER

重试机制:

  • 消费失败的消息会重新投递
  • 默认重试 16 次
  • 超过重试次数进入死信队列

最佳实践:

  • ✅ 保证消费逻辑的幂等性
  • ✅ 合理设置重试次数
  • ✅ 监控死信队列

9. RocketMQ 顺序消息

(1)全局顺序消息

定义:某个 Topic 下的所有消息都要保证顺序。

实现方式:

  • 将 Topic 的读写队列数(queue)设置为 1
  • Producer 和 Consumer 的并发数也要设置为 1 (读写都是单机)

缺点:

  • ⚠️ 并发度低
  • ⚠️ 吞吐量低

(2)分区顺序消息

定义:对于指定的一个 Topic,所有消息根据 Sharding Key(一般是业务唯一键) 进行区块分区。

实现方式:

  • 同一个分区(queue)内的消息按照严格的 FIFO 顺序进行发布和消费
  • Sharding Key 是顺序消息中用来区分不同分区的关键字段
  • 和普通消息的 Key 是完全不同的概念
  • 一个队列只会被一台机器的一个线程消费

适用场景:

  • ✅ 电商的订单创建、支付、退款等流程
  • ✅ 需要保证同一订单的消息顺序

10. RocketMQ 事务消息

(1)事务消息原理

定义:RocketMQ 提供类似 X/Open XA 的分布式事务功能。

实现流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
1. Producer 发送半消息(Half Message)到 Broker
   ↓
2. Broker 返回发送结果
   ↓
3. Producer 执行本地事务
   ↓
4. Producer 根据本地事务结果发送 Commit 或 Rollback
   ↓
5. Broker 收到 Commit:将半消息标记为可投递,Consumer 可消费
   Broker 收到 Rollback:删除半消息
   ↓
6. 如果 Broker 长时间未收到 Commit/Rollback
   Broker 会回查 Producer 的事务状态

(2)事务消息状态

状态说明
TransactionStatus.CommitTransaction提交事务,允许消费者消费此消息
TransactionStatus.RollbackTransaction回滚事务,消息将被删除,不允许消费
TransactionStatus.Unknown中间状态,表示需要 MQ 回查才能确定状态

(3)适用场景

  • ✅ 需要保证本地事务和消息发送的原子性
  • ✅ 例如:订单创建成功后发送消息

四、Java 消息中间件面试题

1. 如何保证消息不被重复消费(幂等性)

问题产生原因

  • 消费者消费完消息后,在给 MQ 确认消息时,网络中断
  • MQ 没有收到确认消息,会认为消息消费失败
  • 会重新推送消息给消费者

解决方案

(1)数据库唯一键约束

  • ✅ 在数据库中创建唯一键
  • 重复插入时会抛出异常

(2)Redis 的 setnx

  • ✅ 使用消息 ID 作为 key
  • 消费前先 setnx
  • 如果返回 0 说明已经消费过

(3)全局唯一 ID + Redis

  • ✅ 给每条消息生成全局唯一 ID
  • 消费前先查询 Redis 是否存在
  • 不存在则消费,并将 ID 存入 Redis

(4)业务逻辑保证幂等性

  • ✅ 设计业务逻辑时保证幂等性
  • 例如:使用状态机,只有在特定状态才能执行操作

2. 如何保证消息不丢失(可靠性)

(1)Producer 端

问题:

  • 消息发送失败
  • 网络抖动导致消息丢失

解决方案:

  • ✅ 使用同步发送模式
  • ✅ 使用异步发送 + 回调确认
  • ✅ 配置重试机制
  • ✅ 记录发送失败的消息,人工补偿

(2)Broker 端

问题:

  • Broker 宕机导致消息丢失
  • 消息未持久化到磁盘

解决方案:

  • ✅ 配置主从复制(Master-Slave)
  • ✅ 使用同步刷盘(重要消息)
  • ✅ 使用同步复制(重要消息)
  • ✅ 配置多副本

(3)Consumer 端

问题:

  • 消费者拿到消息后,还没处理完就宕机了

解决方案:

  • ✅ 手动提交 offset
  • ✅ 处理完业务逻辑后再确认消息
  • ✅ 使用事务消息

3. 如何保证消息的顺序性

问题产生原因

  • 一个 Topic 有多个 Queue
  • 多个 Consumer 并发消费
  • 消息可能被分配到不同的 Queue
  • 不同的 Consumer 消费速度不一样

解决方案

(1)全局顺序

  • 设置 Topic 只有一个 Queue
  • Producer 和 Consumer 都只有一个实例
  • ⚠️ 缺点:吞吐量低

(2)局部顺序(推荐)

  • 根据业务 Key(如订单 ID)进行 Hash
  • 将同一个 Key 的消息发送到同一个 Queue
  • 同一个 Queue 只被一个 Consumer 消费
  • Consumer 内部单线程消费

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Producer 端
SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
    @Override
    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
        // 根据订单 ID 选择队列
        Long orderId = (Long) arg;
        long index = orderId % mqs.size();
        return mqs.get((int) index);
    }
}, orderId);

// Consumer 端使用 MessageListenerOrderly
consumer.registerMessageListener(new MessageListenerOrderly() {
    @Override
    public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, 
                                               ConsumeOrderlyContext context) {
        // 顺序消费消息
        return ConsumeOrderlyStatus.SUCCESS;
    }
});

4. 如何处理消息积压

问题产生原因

  • Consumer 消费速度跟不上 Producer 生产速度
  • Consumer 出现故障,停止消费
  • 消息量突然暴增

解决方案

(1)临时扩容

  • ✅ 增加 Consumer 实例数量
  • ✅ 增加 Queue 数量(需要重启)
  • ✅ 提高单个 Consumer 的消费速度

(2)优化消费逻辑

  • ✅ 优化业务代码,提高处理速度
  • ✅ 使用批量消费
  • ✅ 异步处理,提高并发度

(3)降级处理

  • ✅ 将消息转发到临时 Topic
  • ✅ 部署大量 Consumer 快速消费
  • ✅ 消费完后再恢复正常

(4)丢弃消息

  • ⚠️ 非重要消息可以考虑丢弃
  • ⚠️ 需要业务方确认

5. 如何设计一个消息队列

核心功能

(1)消息生产

  • 支持同步/异步发送
  • 支持批量发送
  • 支持事务消息

(2)消息存储

  • 持久化存储
  • 支持高吞吐量
  • 支持消息查询

(3)消息消费

  • 支持推/拉模式
  • 支持集群/广播消费
  • 支持顺序消费

(4)高可用

  • 主从复制
  • 故障转移
  • 负载均衡

(5)消息可靠性

  • 消息不丢失
  • 消息不重复
  • 消息有序

技术选型

存储层:

  • 使用文件系统存储(顺序写,性能高)
  • 使用内存映射文件(mmap)
  • 使用零拷贝技术

网络层:

  • 使用 Netty 实现高性能网络通信
  • 使用长连接
  • 使用心跳机制

协调层:

  • 使用 NameServer 或 ZooKeeper
  • 实现服务发现和路由

6. RabbitMQ 和 Kafka 的区别

特性RabbitMQKafka
架构基于 AMQP 协议基于日志的分布式系统
吞吐量万级百万级
消息顺序支持分区内有序
消息路由支持复杂路由不支持
消息查询不支持支持按 offset 查询
消息回溯不支持支持
适用场景业务消息日志、大数据

7. 什么是死信队列

定义

死信队列(Dead Letter Queue,DLQ)是用来保存处理失败或过期的消息。


消息进入死信队列的情况

(1)消息被拒绝

  • Consumer 拒绝消费消息
  • 并且没有重新入队

(2)消息过期

  • 消息在队列中的存活时间超过设置的 TTL 时间

(3)队列达到最大长度

  • 队列满了,无法再添加消息

(4)消费失败次数超过阈值

  • 消息重试次数超过最大重试次数

死信队列的作用

  • ✅ 保存异常消息,便于排查问题
  • ✅ 防止消息丢失
  • ✅ 可以对死信消息进行人工处理或告警

8. 如何保证消息队列的高可用

(1)集群部署

  • ✅ 部署多个 Broker 节点
  • ✅ 使用主从复制
  • ✅ 使用多副本机制

(2)故障转移

  • ✅ Master 宕机后,Slave 自动升级为 Master
  • ✅ 使用 NameServer 或 ZooKeeper 进行协调

(3)负载均衡

  • ✅ Producer 轮询发送消息到不同的 Broker
  • ✅ Consumer 均衡消费不同的 Queue

(4)监控告警

  • ✅ 监控 Broker 的健康状态
  • ✅ 监控消息积压情况
  • ✅ 监控消费延迟
  • ✅ 及时告警和处理异常

9. 消息队列如何选型

考虑因素

(1)业务场景

  • 是否需要高吞吐量
  • 是否需要消息顺序
  • 是否需要消息查询

(2)团队技术栈

  • 团队对哪种 MQ 更熟悉
  • 是否有运维经验

(3)社区活跃度

  • 文档是否完善
  • 是否有成熟的解决方案

(4)成本

  • 硬件成本
  • 人力成本
  • 学习成本

选型建议

场景推荐 MQ
中小型公司,业务量不大RabbitMQ
大型公司,业务量大,可靠性要求高RocketMQ
大数据场景,日志采集Kafka
需要复杂路由RabbitMQ
需要超高吞吐量Kafka
需要事务消息RocketMQ

10. 延迟消息如何实现

RocketMQ 实现方式

(1)使用延迟级别

RocketMQ 支持 18 个延迟级别:

1
1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

示例代码:

1
2
3
4
Message msg = new Message("TopicTest", "TagA", "Hello RocketMQ".getBytes());
// 设置延迟级别为 3,对应延迟时间为 10s
msg.setDelayTimeLevel(3);
producer.send(msg);

(2)原理

  1. Producer 发送延迟消息到 Broker
  2. Broker 将消息存储到特殊的延迟队列
  3. 定时任务扫描延迟队列
  4. 到期后将消息投递到真正的 Topic

其他实现方式

(1)使用定时任务

  • 将消息存储到数据库
  • 定时任务扫描到期消息
  • 发送到 MQ

(2)使用 Redis

  • 使用 Redis 的 Sorted Set
  • 以时间戳作为 score
  • 定时扫描到期消息

📚 参考资料:本文内容基于个人学习笔记以及阿里巴巴RocketMQ团队的文档。

本文由作者按照 CC BY 4.0 进行授权