🚀 RocketMQ DLedger:Raft 协议实现 源码详解
RocketMQ 在 4.5+ 版本引入了 DLedger(Distributed Ledger) 模块,基于 Raft 一致性协议 实现了自动选主、强一致性、高可用的存储架构,解决了传统主从模式缺乏自动故障转移的问题。
本文将深入 DLedger 源码(dledger 模块),解析其如何实现 Raft 协议的核心机制:Leader 选举、日志复制、安全性保证。
一、DLedger 架构概览
+-------------+ +-------------+ +-------------+
| Node-1 |<----->| Node-2 |<----->| Node-3 |
| (Follower) | | (Leader) | | (Follower) |
+-------------+ +-------------+ +-------------+
↑ ↑ ↑
| Heartbeat |
+-------- Replicate Log Entries ----------->+
- DLedger Group:由 3 或 5 个节点组成,基于 Raft 协议运行
- Leader:唯一可写节点,处理所有写请求
- Follower:只读,从 Leader 同步数据
- 自动选主:Leader 宕机后自动选举新 Leader
✅ DLedger 是 RocketMQ 实现 真·高可用 的核心组件。
二、源码环境准备
- 源码地址:https://github.com/openmessaging/openmessaging-storage-dledger
- 分支:
master(独立项目,被 RocketMQ 引用) - 核心模块:
dledger:核心 Raft 实现entry:日志条目quorum:多数派确认statemachine:状态机
三、Raft 协议核心机制回顾
| 机制 | 说明 |
|---|---|
| Leader 选举 | 节点通过投票选出 Leader |
| 日志复制 | Leader 将日志复制给 Follower,多数派确认后提交 |
| 安全性 | 保证已提交日志不会被覆盖 |
| 任期(Term) | 每次选举生成新 Term,防止脑裂 |
四、1. Leader 选举(Leader Election)
📌 触发时机:
- 初始启动
- Leader 心跳超时
- 节点网络分区恢复
🔧 入口:DLedgerLeaderElector#handleHeartbeat
// DLedgerLeaderElector.java
public void handleHeartbeat(DLedgerHeartBeatRequest request) {
if (request.getTerm() > dLedgerLeaderElector.getTerm()) {
// 收到更高 Term → 转为 Follower
changeToCandidate(request.getTerm());
}
}
🔧 选举流程:ElectingState#elect
// ElectingState.java
public void elect() {
// 1. 自增 Term
long term = dLedgerLeaderElector.getTerm() + 1;
dLedgerLeaderElector.setTerm(term);
// 2. 投票给自己
votesMap.put(dLedgerServer.getMemberId(), new VoteResultEntry(true, System.currentTimeMillis()));
// 3. 向其他节点发送投票请求
for (String peer : peers) {
sendVoteRequest(peer, term);
}
// 4. 等待投票结果
waitForVotes();
}
🔧 投票请求:VoteRequest
// VoteRequest.java
public class VoteRequest extends DLedgerRequest {
private long term;
private String candidateId;
private long lastLogIndex;
private long lastLogTerm;
}
投票规则(安全性):
- 同一 Term 只能投一次票
- 只能投给日志更完整(
lastLogIndex更大)的节点 - 收到更高 Term 的请求 → 转为 Follower
🔧 投票响应:VoteResponse
// VoteResponse.java
public class VoteResponse extends DLedgerResponse {
private boolean voteGranted; // 是否同意
private long term; // 当前 Term
}
🔧 选举成功条件:
// QuorumUtil.java
public boolean isQuorum(int grantedVotes) {
return grantedVotes > peers.size() / 2; // 多数派同意
}
✅ 3 节点允许 1 个故障,5 节点允许 2 个。
五、2. 日志复制(Log Replication)
📌 流程:
- 客户端写入 Leader
- Leader 写入本地日志
- 并行发送 AppendEntries 给 Follower
- 多数派确认后,提交日志
- 返回客户端
🔧 入口:DLedgerServer#handleAppend
// DLedgerServer.java
public CompletableFuture<AppendEntryResponse> handleAppend(AppendEntryRequest request) {
if (role != Role.LEADER) {
// 非 Leader → 转发给 Leader
return forwardToLeader(request);
}
// 写入本地日志
DLedgerEntry entry = new DLedgerEntry();
entry.setData(request.getBody());
entry.setIndex(dLedgerStore.getLedgerEndIndex() + 1);
entry.setTerm(request.getTerm());
dLedgerStore.append(entry);
// 并行复制给 Follower
return replicateToFollowers(entry);
}
🔧 复制逻辑:DLedgerReplicator#replicate
// DLedgerReplicator.java
private CompletableFuture<AppendEntryResponse> replicate(DLedgerEntry entry) {
// 1. 并行发送 AppendEntries 给所有 Follower
List<CompletableFuture<AppendEntryResponse>> futures = new ArrayList<>();
for (String peer : peers) {
futures.add(sendAppendEntries(peer, entry));
}
// 2. 等待多数派确认
return QuorumFuture.wrap(futures, peers.size() / 2 + 1);
}
🔧 追加请求:AppendEntryRequest
// AppendEntryRequest.java
public class AppendEntryRequest extends DLedgerRequest {
private long term;
private String leaderId;
private long prevLogIndex;
private long prevLogTerm;
private List<ByteBuf> entries; // 日志条目
private long leaderCommit;
}
日志一致性检查:
prevLogIndex和prevLogTerm必须匹配,否则拒绝- 保证日志连续性
🔧 提交日志:DLedgerCommitLog#commit
// DLedgerCommitLog.java
public void commit(long commitIndex) {
while (lastCommittedIndex < commitIndex) {
DLedgerEntry entry = dLedgerStore.get(++lastCommittedIndex);
// 通知状态机(如 RocketMQ 的 CommitLog)
stateMachine.onCommitted(entry);
}
}
✅ 提交后,消息才可被消费。
六、3. 安全性与状态机
📌 安全性保证:
- 选举限制:只投票给日志更完整的节点
- 日志匹配:保证 Leader 和 Follower 日志一致
- Leader 完整性:Leader 拥有所有已提交日志
🔧 状态机:StateMachine
// StateMachine.java
public interface StateMachine {
void onApply(DLedgerEntry entry); // 应用日志
void onCommitted(DLedgerEntry entry); // 提交日志
void onSnapshotSave(); // 保存快照
void onSnapshotLoad(); // 加载快照
}
RocketMQ 集成:
DLedgerCommitLog作为状态机,将日志写入 RocketMQ 的 CommitLog- 实现“日志复制 + 消息存储”的无缝衔接
七、DLedger 与 RocketMQ 集成
📌 RocketMQ 使用 DLedger 作为存储引擎:
# broker.conf
enableDLegerCommitLog=true
dLegerGroup=raft_group_01
dLegerPeers=n0-192.168.0.10:40911;n1-192.168.0.11:40911;n2-192.168.0.12:40911
dLegerSelfId=n0
🔧 启动流程:
BrokerController检测到enableDLegerCommitLog=true- 创建
DLedgerCommitLog替代传统CommitLog - 启动
DLedgerServer,加入 Raft Group - 自动选举 Leader,开始服务
八、总结:DLedger 核心机制源码链路
1. Leader 选举
心跳超时 → 转为 Candidate
↓
自增 Term → 投票给自己
↓
发送 VoteRequest → 收集投票
↓
多数派同意 → 成为 Leader
2. 日志复制
客户端写入 Leader
↓
Leader 写本地日志
↓
并行发送 AppendEntries
↓
多数派确认 → 提交日志
↓
返回客户端
3. 安全性
投票限制:只投给日志更完整的节点
日志匹配:prevLogIndex/prevLogTerm 必须一致
Leader 完整性:Leader 拥有所有已提交日志
✅ 总结
| 机制 | 核心类 | 说明 |
|---|---|---|
| Leader 选举 | DLedgerLeaderElector | 基于 Term 和投票 |
| 日志复制 | DLedgerReplicator | 并行复制 + 多数派确认 |
| 状态机 | StateMachine | 与 RocketMQ CommitLog 集成 |
| 网络通信 | DLedgerRpcNettyService | Netty 实现 RPC |
🚀 一句话总结:
RocketMQ DLedger 通过 Raft 协议 实现了 “自动选主 + 强一致性 + 高可用”,
彻底解决了传统主从模式的单点故障问题,
是 RocketMQ 迈向云原生、高可用架构的关键一步。
掌握 DLedger 源码,你就能理解 RocketMQ 如何在分布式环境下保证数据一致性和服务连续性,为构建可靠消息系统打下坚实基础。
RocketMQ DLedger:Raft协议源码详解
2455

被折叠的 条评论
为什么被折叠?



