1.架构图
RocketMQ的四层架构:
1.1 客户端层(Clients):
Producer集群:
(1)DefaultMQProducer:普通消息生产者
(2)TransactionMQProducer:事务消息生产者
(3)支持同步、异步、单向发送
Consumer集群:
(1)DefaultMQPushConsumer:服务端推送模式
(2)DefaultMQPullConsumer:客户端拉取模式
(3)支持集群消费和广播消费
1.2 命名服务层(NameServer)
(1)无状态设计,多节点互不通信
(2)Broker管理:
- 接收Broker心跳注册(每30s)
- 检查Broker存活状态(每10s)
- 剔除不活跃的Broker(120s未收到心跳)
(3)路由管理:
- 维护Topic队列信息
- 维护Broker存活信息
- 提供路由信息查询服务
(4)提供服务发现能力
(5)Producer和Consumer会连接多个NameServer
1.3 消息服务层(Brokers)
(1)Broker Master:提供读写服务
(2)Broker Slave:提供读服务
(3)异步刷盘和同步刷盘
1.4 存储层(Store)
(1)CommitLog:消息存储文件
(2) ConsumeQueue:消息消费队列,相当于CommitLog索引
(3) IndexFile:消息索引文件,提供消息Key查询
2.RocketMQ的核心流程
RocketMQ的核心流程,主要包括生产、存储、消费三大环节
2.1消息生产流程
// 核心代码结构
public class DefaultMQProducer {
private SendResult send(Message msg) {
// 1. 检查消息
this.checkMessage(msg);
// 2. 查找Topic路由信息
TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
// 3. 选择消息队列(默认轮询策略)
MessageQueue messageQueue = selectOneMessageQueue(topicPublishInfo);
// 4. 发送消息
SendResult sendResult = this.sendKernelImpl(msg, messageQueue, communicationMode, sendCallback, timeout);
}
}
2.2消息存储流程:
// Broker存储核心代码
public class CommitLog {
public PutMessageResult putMessage(MessageExtBrokerInner msg) {
// 1. 获取写入锁
putMessageLock.lock();
try {
// 2. 验证Broker状态
if (!this.checkMessage(msg)) {
return null;
}
// 3. 写入消息
result = this.mappedFile.appendMessage(msg);
// 4. 刷盘
handleDiskFlush(result);
// 5. 复制到Slave
handleHA(result);
} finally {
putMessageLock.unlock();
}
}
}
存储结构:
- commitLog:
|-- 00000000000000000000 (文件名代表偏移量)
|-- 00000000001073741824
|-- ...
- consumeQueue:
|-- topic
|-- queue0
|-- 00000000000000000000
|-- 00000000000000000001
|-- queue1
|-- ...
- index:
|-- 20240205123456789
|-- 20240205234567890
2.3消息消费流程
public class DefaultMQPushConsumerImpl {
private void pullMessage(final PullRequest pullRequest) {
// 1. 构建消费请求
PullMessageRequestHeader requestHeader = new PullMessageRequestHeader();
requestHeader.setConsumerGroup(this.consumerGroup);
requestHeader.setTopic(pullRequest.getMessageQueue().getTopic());
requestHeader.setQueueId(pullRequest.getMessageQueue().getQueueId());
requestHeader.setQueueOffset(pullRequest.getNextOffset());
// 2. 执行拉取
PullCallback pullCallback = new PullCallback() {
@Override
public void onSuccess(PullResult pullResult) {
// 处理拉取结果
processQueue.putMessage(pullResult.getMsgFoundList());
// 提交消费请求
consumeMessageService.submitConsumeRequest(
pullResult.getMsgFoundList(),
processQueue,
pullRequest.getMessageQueue());
}
};
}
}
消费者重平衡流程:
public class RebalanceImpl {
private void rebalanceByTopic() {
// 1. 获取Topic下所有消费者
List<String> cidAll = this.mQClientFactory.findConsumerIdList(topic);
// 2. 对消费者排序
Collections.sort(cidAll);
// 3. 分配队列
AllocateMessageQueueStrategy strategy = this.allocateMessageQueueStrategy;
List<MessageQueue> allocateResult = strategy.allocate(
this.consumerGroup,
this.mQClientFactory.getClientId(),
mqAll,
cidAll);
}
}
3.生产者三种发送方式
a. 同步发送:
Producer → 等待Broker响应 → 继续业务流程
b. 异步发送:
Producer → 注册回调函数 → 继续业务流程
↓
收到响应后触发回调
c. 单向发送:
Producer → 发送消息 → 继续业务流程(不关心结果)
4.故障处理
a. Broker故障:
- 自动切换到其他Broker
- 触发重试机制
- 更新故障延迟信息
b. 网络故障:
- 通过重试机制保证消息投递
- 支持消息轨迹查询
5.连接方式
各个角色之间的连接关系:
Producer -> NameServer:长连接
Producer -> Broker:长连接
Consumer -> NameServer:长连接
Consumer -> Broker:长连接
Broker -> NameServer:长连接(心跳)
连接断开处理:
如果心跳超时(默认120秒):
- NameServer会删除Broker的路由信息
- Producer会重新建立连接
- Consumer会触发重平衡
长连接机制的优点:
(1)减少连接建立的开销
(2)提高消息收发效率
(3)及时感知各个组件的存活状态
(4)保证集群的可用性和稳定性
6.关键的性能优化点
// 1. 文件预分配
public void preallocation() {
File file = new File(storePath + File.separator + nextFilePath);
try {
file.createNewFile();
RandomAccessFile raFile = new RandomAccessFile(file, "rw");
raFile.setLength(fileSize);
}
}
// 2. 零拷贝发送
public SendResult sendMessage(final Channel channel, final Message msg) {
// 使用FileChannel.transferTo实现零拷贝
fileChannel.transferTo(position, count, socketChannel);
}
// 3. 批量消息处理
public PullResult pullMessage(final int batchSize) {
// 一次拉取多条消息
List<MessageExt> msgFoundList = new ArrayList<>(batchSize);
}