《消息中间件知识个人笔记》
一、消息中间件概述
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
| 特性 | RabbitMQ | RocketMQ | Kafka |
|---|---|---|---|
| 开发语言 | Erlang | Java | Scala/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 发送消息
步骤:
- Producer 启动时先跟 NameServer 集群中的其中一台建立长连接
- 并从 NameServer 中获取当前发送的 Topic 存在哪些 Broker 上
- 轮询从队列列表中选择一个队列
- 然后与队列所在的 Broker 建立长连接从而向 Broker 发消息
(5)Consumer 消费消息
步骤:
- Consumer 启动时先跟 NameServer 集群中的其中一台建立长连接
- 并从 NameServer 中获取当前订阅 Topic 存在哪些 Broker 上
- 然后直接跟 Broker 建立连接通道
- 开始消费消息
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 的区别
| 特性 | RabbitMQ | Kafka |
|---|---|---|
| 架构 | 基于 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)原理
- Producer 发送延迟消息到 Broker
- Broker 将消息存储到特殊的延迟队列
- 定时任务扫描延迟队列
- 到期后将消息投递到真正的 Topic
其他实现方式
(1)使用定时任务
- 将消息存储到数据库
- 定时任务扫描到期消息
- 发送到 MQ
(2)使用 Redis
- 使用 Redis 的 Sorted Set
- 以时间戳作为 score
- 定时扫描到期消息
📚 参考资料:本文内容基于个人学习笔记以及阿里巴巴RocketMQ团队的文档。