一、RocketMQ 基础
1. RocketMQ 是什么?
# 核心特点:
- 阿里开源,Apache顶级项目
- 分布式消息中间件
- 高可用、高吞吐、低延迟
- 支持发布订阅、顺序消息、事务消息
- 支持消息轨迹、消息过滤
2. RocketMQ 架构
# 核心组件:
1. NameServer: 无状态注册中心
- 服务发现与路由管理
- 轻量级,集群部署
2. Broker: 消息存储和转发
- 主从架构,支持同步/异步复制
- 消息存储、查询、消费队列管理
3. Producer: 消息生产者
- 与NameServer建立长连接
- 从NameServer获取路由信息
4. Consumer: 消息消费者
- Push/Pull两种模式
- 集群/广播两种消费模式
3. RocketMQ 与其他MQ对比
| 特性 | RocketMQ | Kafka | RabbitMQ | ActiveMQ |
|---|---|---|---|---|
| 设计目标 | 金融级 | 日志处理 | 企业级 | 传统消息 |
| 吞吐量 | 极高 | 极高 | 高 | 中等 |
| 延迟 | 毫秒级 | 毫秒级 | 微秒级 | 毫秒级 |
| 可用性 | 非常高 | 高 | 高 | 中等 |
| 事务消息 | 支持 | 不支持 | 支持 | 支持 |
| 消息顺序 | 支持 | 支持 | 不支持 | 支持 |
| 消息追溯 | 支持 | 支持 | 有限 | 有限 |
| 成熟度 | 高 | 极高 | 极高 | 高 |
二、NameServer
1. NameServer 作用
# 核心功能:
1. 服务注册:Broker启动时注册
2. 服务发现:Producer/Consumer获取路由
3. 心跳检测:监控Broker存活状态
4. 路由管理:维护Topic-Broker映射
# 特点:
- 无状态设计,可水平扩展
- 数据存储在内存
- 通过心跳维持数据
- 部署奇数台(2n+1)
2. NameServer 工作流程
// 1. Broker注册
Broker → NameServer: 注册Topic、Broker、Queue信息
// 2. Producer/Consumer查询
Producer → NameServer: 查询Topic路由
// 3. 心跳机制
Broker → NameServer: 每30秒发送心跳
// 4. 故障检测
NameServer: 120秒无心跳,剔除Broker
三、Broker
1. Broker 架构
# 核心模块:
1. Remoting: 网络通信层
2. Client Manager: 客户端管理
3. Store Service: 存储服务
4. HA Service: 高可用服务
5. Index Service: 索引服务
# 存储结构:
Broker
├── CommitLog # 所有消息顺序存储
├── ConsumeQueue # 逻辑消费队列
├── IndexFile # 消息索引
└── Config # 配置信息
2. 消息存储机制
# 存储流程:
1. 消息写入CommitLog(顺序写)
2. 异步构建ConsumeQueue
3. 异步构建IndexFile
# 文件结构:
CommitLog: 1GB固定大小,顺序写入
ConsumeQueue: 30万条记录,固定大小
IndexFile: 哈希索引,支持快速查找
3. 主从复制
# 复制方式:
1. 同步复制:主从都写成功返回ACK
2. 异步复制:主成功即返回
# 配置:
brokerRole:
- ASYNC_MASTER # 异步复制主节点
- SYNC_MASTER # 同步复制主节点
- SLAVE # 从节点
四、Producer
1. 生产者类型
// 1. 普通生产者
DefaultMQProducer producer = new DefaultMQProducer("ProducerGroup");
producer.send(message);
// 2. 事务消息生产者
TransactionMQProducer producer = new TransactionMQProducer("TranProducerGroup");
producer.setTransactionListener(new TransactionListenerImpl());
// 3. 延迟消息生产者
message.setDelayTimeLevel(3); // 延迟级别
2. 发送模式
// 1. 同步发送
SendResult result = producer.send(message);
// 2. 异步发送
producer.send(message, new SendCallback() {
public void onSuccess(SendResult result) {}
public void onException(Throwable e) {}
});
// 3. 单向发送
producer.sendOneway(message);
3. 消息类型
// 1. 普通消息
Message msg = new Message("Topic", "Tag", "Key", body);
// 2. 顺序消息
MessageQueueSelector selector = new MessageQueueSelector() {
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
Integer id = (Integer) arg;
int index = id % mqs.size();
return mqs.get(index);
}
};
producer.send(msg, selector, orderId);
// 3. 事务消息
TransactionSendResult result = producer.sendMessageInTransaction(msg, null);
// 4. 定时/延迟消息
msg.setDelayTimeLevel(3); // 延迟10秒
五、Consumer
1. 消费者类型
// 1. 推模式(推荐)
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("ConsumerGroup");
consumer.registerMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
// 2. 拉模式
DefaultLitePullConsumer consumer = new DefaultLitePullConsumer("PullConsumerGroup");
2. 消费模式
// 1. 集群消费(默认)
consumer.setMessageModel(MessageModel.CLUSTERING);
// 2. 广播消费
consumer.setMessageModel(MessageModel.BROADCASTING);
3. 消费进度管理
# 进度存储:
- 集群模式:Broker存储
- 广播模式:本地存储
# 重置消费位点:
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
// CONSUME_FROM_LAST_OFFSET 最后位置
// CONSUME_FROM_FIRST_OFFSET 开始位置
// CONSUME_FROM_TIMESTAMP 指定时间
六、消息存储与清理
1. 存储流程
# 写入流程:
1. Producer发送消息到Broker
2. Broker写入CommitLog(顺序写)
3. 异步构建ConsumeQueue
4. 异步构建IndexFile
# 读取流程:
1. Consumer发送拉取请求
2. Broker查询ConsumeQueue获取CommitLog偏移量
3. 从CommitLog读取消息
4. 返回给Consumer
2. 文件清理策略
# 清理条件:
1. 文件过期:默认72小时
2. 磁盘空间不足
3. 手动触发清理
# 配置:
fileReservedTime=72 # 文件保留时间(小时)
deleteWhen=04 # 清理时间(凌晨4点)
diskMaxUsedSpaceRatio=75 # 最大磁盘使用率
七、顺序消息
1. 顺序消息原理
# 全局顺序:
- Topic只有一个队列
- 严格保证全局顺序
- 性能较低
# 分区顺序:
- 同一业务ID的消息发到同一队列
- 同一队列内保证顺序
- 使用MessageQueueSelector
2. 顺序消费实现
// 生产者
producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
Long orderId = (Long) arg;
long index = orderId % mqs.size();
return mqs.get((int) index);
}
}, orderId);
// 消费者(顺序监听器)
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeOrderlyContext context) {
// 顺序处理
return ConsumeOrderlyStatus.SUCCESS;
}
});
八、事务消息
1. 事务消息流程
# 两阶段提交:
1. 发送half消息
2. 执行本地事务
3. 提交/回滚消息
# 状态检查:
- Broker定时检查half消息状态
- 回调生产者检查事务状态
2. 事务消息实现
// 事务监听器
public class TransactionListenerImpl implements TransactionListener {
// 执行本地事务
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
try {
// 执行本地业务
boolean success = doBusiness();
return success ? LocalTransactionState.COMMIT_MESSAGE
: LocalTransactionState.ROLLBACK_MESSAGE;
} catch (Exception e) {
return LocalTransactionState.UNKNOW;
}
}
// 检查本地事务状态
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
// 检查业务状态
return checkBusinessStatus() ? LocalTransactionState.COMMIT_MESSAGE
: LocalTransactionState.ROLLBACK_MESSAGE;
}
}
九、延迟消息
1. 延迟级别
# 18个固定级别:
1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
# 实现原理:
1. 发送延迟消息
2. Broker将消息存入SCHEDULE_TOPIC_XX队列
3. 定时任务扫描到期消息
4. 投递到目标Topic
2. 自定义延迟
// 不支持自定义延迟时间
// 可通过以下方式模拟:
1. 使用定时任务+普通消息
2. 使用Redis ZSet实现
3. 使用时间轮算法
十、消息过滤
1. 过滤方式
# 1. Tag过滤(推荐)
- 一个消息一个Tag
- 消费者订阅Tag表达式
- 如: TagA || TagB
# 2. SQL过滤
- 基于消息属性过滤
- 支持SQL92语法
- 如: a > 5 AND b = 'abc'
# 3. 类过滤
- 实现MessageFilter接口
- 完全自定义过滤逻辑
2. Tag过滤示例
// 生产者
Message msg = new Message("Topic", "TagA", "Key", body);
// 消费者
consumer.subscribe("Topic", "TagA || TagB");
// 不推荐:* 表示订阅所有Tag
consumer.subscribe("Topic", "*");
十一、高可用与负载均衡
1. Producer负载均衡
// 默认策略:轮询
List<MessageQueue> mqs = producer.fetchPublishMessageQueues("Topic");
MessageQueue mq = mqs.get(index % mqs.size());
// 自定义策略
producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
// 自定义选择逻辑
return mqs.get(hash(arg) % mqs.size());
}
}, shardingKey);
2. Consumer负载均衡
# 集群模式负载均衡:
1. 获取Topic所有队列
2. 获取消费者组所有消费者
3. 平均分配队列
4. 每20秒重新平衡
# 分配策略:
- 平均分配(默认)
- 环形分配
- 机房就近分配
- 一致性哈希分配
3. Broker高可用
# 主从架构:
- 一主多从
- 同步/异步复制
- 自动故障转移
# 部署建议:
- 至少2主2从
- 跨机房部署
- 监控报警
十二、消息轨迹
1. 轨迹数据
# 记录信息:
1. 生产者轨迹
- 发送时间
- 发送状态
- 发送IP
2. 消费者轨迹
- 消费时间
- 消费状态
- 消费IP
- 重试次数
2. 轨迹配置
# 开启轨迹
traceTopicEnable=true
traceTopicName=RMQ_SYS_TRACE_TOPIC
# 轨迹存储
- 默认存储到Broker
- 可自定义存储
- 支持可视化查询
十三、性能优化
1. 生产者优化
// 1. 批量发送
List<Message> messages = new ArrayList<>();
for (int i = 0; i < 100; i++) {
messages.add(new Message("Topic", "Tag", "Key", body));
}
SendResult result = producer.send(messages);
// 2. 压缩消息
message.setCompressed(true);
// 3. 合理设置Topic队列数
// 建议:队列数 = 生产者数 * 消费者数
2. 消费者优化
// 1. 批量消费
consumer.setConsumeMessageBatchMaxSize(32);
// 2. 并发消费
consumer.setConsumeThreadMin(20);
consumer.setConsumeThreadMax(64);
// 3. 拉取批量
consumer.setPullBatchSize(32);
3. Broker优化
# 1. 存储优化
flushDiskType=ASYNC_FLUSH # 异步刷盘
flushCommitLogLeastPages=4 # 最少页数
# 2. 内存优化
mappedFileSizeCommitLog=1073741824 # 1GB
mappedFileSizeConsumeQueue=300000 # 30万条
# 3. 网络优化
sendMessageThreadPoolNums=16
pullMessageThreadPoolNums=16
十四、监控与运维
1. 监控指标
# 关键指标:
1. 消息堆积
- 监控Topic消费延迟
2. 生产/消费TPS
- 监控消息吞吐量
3. 系统资源
- CPU、内存、磁盘、网络
4. 错误率
- 发送失败率
- 消费失败率
2. 运维命令
# 1. 集群状态
mqadmin clusterList -n localhost:9876
# 2. Topic状态
mqadmin topicStatus -n localhost:9876 -t TopicTest
# 3. 消费进度
mqadmin consumerProgress -n localhost:9876 -g ConsumerGroup
# 4. 消息查询
mqadmin queryMsgById -n localhost:9876 -i 0A9A003F00002A9F00000000000003B4
# 5. 发送消息
mqadmin sendMsgStatus -n localhost:9876 -t TopicTest -p "hello" -k "key"
十五、常见问题与解决方案
1. 消息丢失
# 可能原因:
1. 异步刷盘丢失
2. 主从复制丢失
3. 消费者处理异常
# 解决方案:
1. 同步刷盘 + 同步复制
2. 事务消息
3. 消费端幂等处理
4. 消息轨迹追踪
2. 消息重复
# 可能原因:
1. 生产者重试
2. 消费者重试
3. Broker重投
# 解决方案:
1. 消费端幂等处理
2. 使用全局唯一ID
3. 记录已处理消息
3. 消息堆积
# 处理方案:
1. 增加消费者实例
2. 提高消费能力
3. 临时Topic分流
4. 跳过堆积消息
5. 设置死信队列
十六、高级特性
1. 消息重试
// 顺序消息不重试
// 普通消息重试机制:
// 重试次数:16次
// 重试间隔:越来越长
// 重试Topic:%RETRY%ConsumerGroup
// 消费失败处理
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
try {
// 处理消息
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
} catch (Exception e) {
// 返回RECONSUME_LATER会重试
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
}
2. 死信队列
# 死信Topic:
%DLQ%ConsumerGroup
# 进入条件:
1. 最大重试次数后仍失败
2. 默认重试16次
# 处理:
1. 监控死信队列
2. 人工干预处理
3. 重新投递
十七、RocketMQ 5.0 新特性
1. 轻量级队列
# 特点:
- 更轻量的消费进度管理
- 支持更高并发
- 简化运维
2. 多副本机制
# DLEDGER模式:
- 基于Raft协议
- 自动选主
- 强一致性
3. 云原生支持
# 特性:
- 容器化部署
- 弹性伸缩
- 服务网格集成
十八、常见面试题
1. 基础问题
Q1: RocketMQ如何保证消息不丢失?
A:
1. 生产者:同步发送+重试
2. Broker:同步刷盘+同步复制
3. 消费者:正确消费后确认
Q2: NameServer在集群中作用?
A:
1. 服务注册与发现
2. 路由信息管理
3. 心跳检测
4. 无状态设计,可水平扩展
Q3: CommitLog和ConsumeQueue区别?
A:
- CommitLog:物理存储,所有消息顺序写入
- ConsumeQueue:逻辑队列,存储消息在CommitLog的偏移
2. 进阶问题
Q4: 如何实现顺序消息?
A:
1. 生产者:相同业务ID发送到同一队列
2. 消费者:顺序消费同一队列
3. 使用MessageQueueSelector和MessageListenerOrderly
Q5: 事务消息实现原理?
A:
1. 发送half消息
2. 执行本地事务
3. 提交/回滚消息
4. Broker定时检查未完成事务
Q6: 如何解决消息堆积?
A:
1. 增加消费者实例
2. 提高批量消费大小
3. 优化消费逻辑
4. 临时扩容Topic队列
5. 设置死信队列
3. 实战问题
Q7: 如何设计消息幂等性?
A:
1. 消息唯一ID
2. 业务状态检查
3. 数据库唯一约束
4. Redis防重表
5. 分布式锁
Q8: 如何选择同步/异步复制?
A:
- 同步复制:金融场景,强一致性
- 异步复制:日志场景,高性能
- 根据业务容忍度选择
Q9: 如何监控RocketMQ集群?
A:
1. 自带监控命令
2. 集成Prometheus
3. 使用RocketMQ-Exporter
4. 配置告警规则
5. 监控关键指标
面试准备建议:
-
深入理解架构设计原理
-
掌握消息存储和流转机制
-
熟悉各种消息类型的使用场景
-
了解高可用和负载均衡策略
-
准备实际项目中的问题解决经验
-
关注新版本特性变化
-
动手搭建和配置集群
高频考点:
-
RocketMQ核心组件与架构
-
消息存储机制(CommitLog/ConsumeQueue)
-
顺序消息实现原理
-
事务消息两阶段提交
-
消息高可用方案
-
消息过滤与路由
-
性能优化策略
-
常见问题排查与解决