raft-rust
独立的 Raft 共识实现(Rust)。
- 库:
step()/tick()驱动的纯协议核心 + 可插拔存储 / 状态机 - 部署:每个节点一份 YAML、一个进程(
raft-node),节点间 TCP + bincode - 客户端:
raft-cli(put / get / scan / status / members)
不是生产级产品:无 TLS、快照整包发送、教学向实现。适合学习 Raft 与本地原型。
深入文档
- docs/README.md — 文档入口
- docs/zh/README.md — 中文专题索引(01–14)
| 想了解 | 从这读 |
|---|---|
| 选举 | 03 |
| put 从从节点到存储 | 04 |
| 复制冲突 / 线性读 / Session | 07 · 08 · 09 |
| 成员变更 / 快照 | 10 · 11 |
| 部署 / 故障 / FAQ | 12 · 13 · 14 |
功能
| 能力 | 说明 |
|---|---|
| 共识 | 选主、日志复制、多数提交 |
| 读 | 领导者线性一致读(多数确认) |
| Pre-vote / CheckQuorum | Options 可开关,减轻分区干扰 |
| 成员变更 | 联合共识(Joint Consensus) |
| 领导转移 | ChangeMembership 可去掉当前领导,Simple 提交后旧领导 step down |
| 写去重 | WriteSession(client_id, seq) + SessionState |
| 快照 | snapshot / InstallSnapshot / 日志 compact_to |
| 存储 | BitCask 日志结构引擎(data_dir/bitcask.log) |
| 网络 | TCP 帧:WireMsg::{Raft, Client, ClientReply} |
| 测试用进程内集群 | cluster::Cluster(channel + 故障注入) |
依赖
- Rust(edition 2024)
- 构建:
cargo build --bins
单节点(最快上手)
配置里 peers: [](或不写同伴),进程启动后立刻成为 Leader。
# 终端 1:节点
# 终端 2:CLI(只连一个地址)
config/single.yaml:
node:
id: 1
listen: "127.0.0.1:7001"
data_dir: "data/single"
peers:
options:
heartbeat_interval: 2
election_timeout_min: 5
election_timeout_max: 10
max_append_entries: 100
pre_vote: true
check_quorum: true
snapshot_threshold: 1000
数据目录:data/single/bitcask.log(已在 .gitignore 中忽略 data/)。
也可:cargo run -- --config config/single.yaml(default-run = raft-node)。
多节点(每节点一份配置)
三份配置、三个进程,例如 config/node1.yaml / node2.yaml / node3.yaml。
# 三个终端分别启动
# CLI:可写多个 peer,失败/Abort 时换节点重试
config/node1.yaml 示例:
node:
id: 1
listen: "127.0.0.1:7001"
data_dir: "data/node1"
peers:
-
-
options:
heartbeat_interval: 2
election_timeout_min: 5
election_timeout_max: 10
max_append_entries: 100
pre_vote: true
check_quorum: true
snapshot_threshold: 1000
节点 2、3 同理:改 id / listen / data_dir,peers 写另外两个地址。
raft-cli 命令
raft-cli --peers host:port[,host:port...] [--client-id UUID] <command>
| 命令 | 说明 |
|---|---|
put <key> <value> |
带 session 的写(幂等序号) |
get <key> |
线性一致读 |
scan |
扫描全部 KV |
status |
领导、term、commit、voters 等 |
members <id,id,...> |
成员变更(目标投票集合) |
客户端 session 文件
- 路径:运行 CLI 时的当前工作目录下的
.raft-cli-session - 内容:
client_id+last_seq(每次put递增) - 作用:超时重试同一写时带相同
(client_id, seq),避免双执行 - 可用
--client-id <uuid>覆盖 id;文件已在.gitignore中
这不是集群配置,也不是节点数据;删了只会让 CLI 当成新客户端重新编号。
配置字段
| 字段 | 含义 |
|---|---|
node.id |
节点 ID(u8,集群内唯一) |
node.listen |
本机监听 ip:port |
node.data_dir |
数据目录(BitCask + 快照元数据) |
peers |
其它节点 { id, addr };空 = 单节点 |
options.heartbeat_interval |
心跳间隔(tick) |
options.election_timeout_min/max |
选举超时范围(tick) |
options.max_append_entries |
单次 Append 最多条数 |
options.pre_vote |
是否 Pre-vote |
options.check_quorum |
领导丢多数是否下台 |
options.snapshot_threshold |
距上次快照 apply 多少条后压缩;0 关闭 |
逻辑时间:TICK_INTERVAL = 100ms。
测试
覆盖范围包括:
- 进程内:选主、转发、分区、杀主、丢包/乱序、并发客户端
- Pre-vote / CheckQuorum
- 成员变更、移除领导(领导转移)
- Session 去重、BitCask
- 重启恢复、快照截断后重启
- 真多进程(
tests/multi_process.rs拉起子进程)
进程内演示(非部署路径):
库内嵌入(可选)
use ;
let cluster = spawn; // 测试用进程内集群
let mut client = cluster.client;
wait_for_leader?;
client.put?;
或直接使用 Node + 自建传输(见 src/lib.rs / src/net)。
目录结构
src/
bin/raft_node.rs 节点进程(单节点或多节点中的一员)
bin/raft_cli.rs 命令行客户端
net/ TCP + bincode
cluster/ 进程内集群(测试 / example)
config.rs 节点 YAML 加载
raft/ 协议:node / log / membership / session / kv
storage/ Engine + BitCask
config/
single.yaml 单节点
node1.yaml … node3.yaml
tests/ 单元与集成测试
examples/kv_cluster.rs 进程内三节点演示
已知限制
- 明文 TCP,无 TLS / 认证(生产可在前面做 TLS 终止)
- 快照整包发送,不分块流式
- 无自动领导优雅迁移 RPC(通过成员变更去掉旧领导实现 step down)
- 非生产级运维能力(监控、动态配置热更新等未做)
许可证
Apache-2.0。实现紧贴 Raft 论文,并包含成员变更、Pre-vote、快照、多进程部署等扩展。