# simple-raft **Repository Path**: sh_wangwanbao/simple-raft ## Basic Information - **Project Name**: simple-raft - **Description**: 一个最小化的Raft实现,参考Nacos JRaft设计,用于学习分布式共识算法。去掉生产级的复杂优化,只保留核心逻辑,500行代码理解Raft本质。已实现:Leader选举、心跳机制、持久化存储。规划中:日志复制、状态机。配套掘金系列文章持续更新。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2025-11-22 - **Last Updated**: 2025-12-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Simple Raft 一个基于 Java 实现的简化版 Raft 分布式一致性协议,用于学习和理解 Raft 算法的核心机制。 ## 项目简介 Simple Raft 是对 [Raft 共识算法](https://raft.github.io/) 的最小化实现,专注于核心功能: - **Leader 选举**:实现随机超时触发的选举机制,保证集群中只有一个 Leader - **心跳机制**:Leader 定期向 Follower 发送心跳,维持领导权 - **任期管理**:通过任期号(term)解决冲突和保证一致性 - **状态持久化**:持久化 `currentTerm` 和 `votedFor`,确保节点重启后的安全性 本项目采用 MVP(最小可行产品)方式实现,暂不包括日志复制、快照等高级特性。 ## 架构设计 ### 模块划分 ``` simple-raft/ ├── surfing-raft-core/ # 核心算法实现 │ ├── model/ # 数据模型(Node、NodeState) │ ├── rpc/ # RPC 请求/响应对象 │ ├── storage/ # 持久化存储 │ └── RaftNode.java # Raft 核心逻辑 ├── surfing-raft-node/ # 节点服务(Spring Boot) │ ├── controller/ # REST API 控制器 │ ├── service/ # 节点服务层 │ └── RaftNodeApplication.java └── surfing-raft-demo/ # 演示和测试模块 ``` ### 核心组件 - **RaftNode**:Raft 核心算法实现,包含状态机、选举、心跳逻辑 - **RaftService**:业务层服务,封装定时任务和 RPC 调用 - **RaftController**:对外 REST API,处理投票请求、心跳和状态查询 - **StatePersistence**:状态持久化接口,支持文件存储 ## 技术栈 - **Java 17** - **Spring Boot 3.0.2** - **Maven 3.x** - **Lombok** - **Jackson**(JSON 序列化) - **SLF4J + Logback**(日志) ## 快速开始 ### 环境要求 - JDK 17+ - Maven 3.6+ - Linux/macOS 或 Windows(需 WSL/Git Bash) ### 构建项目 ```bash cd simple-raft mvn clean package -DskipTests ``` ### 启动集群 使用提供的脚本快速启动 3 节点集群: ```bash # 启动集群 ./start-cluster.sh # 查看集群状态 ./check-status.sh # 停止集群 ./stop-cluster.sh ``` 启动后,三个节点分别运行在: - 节点 1: http://localhost:8081 - 节点 2: http://localhost:8082 - 节点 3: http://localhost:8083 ### 手动启动单个节点 ```bash java -jar surfing/surfing-raft-node/target/surfing-raft-node-1.0.0.jar \ --spring.profiles.active=node1 ``` 配置文件位于 `surfing/surfing-raft-node/src/main/resources/application-node{1,2,3}.properties`。 ## API 接口 ### 查看节点状态 ```bash curl http://localhost:8081/raft/status ``` 响应示例: ```json { "nodeId": "node-1", "state": "LEADER", "currentTerm": 3, "leaderId": "node-1", "votedFor": "node-1" } ``` ### 投票请求(RequestVote RPC) ```bash curl -X POST http://localhost:8081/raft/vote \ -H "Content-Type: application/json" \ -d '{ "term": 2, "candidateId": "node-2", "lastLogIndex": 0, "lastLogTerm": 0 }' ``` ### 心跳请求(AppendEntries RPC) ```bash curl -X POST http://localhost:8081/raft/append \ -H "Content-Type: application/json" \ -d '{ "term": 2, "leaderId": "node-2", "prevLogIndex": 0, "prevLogTerm": 0, "leaderCommit": 0 }' ``` ### 健康检查 ```bash curl http://localhost:8081/health ``` ## 测试验证 ### 运行选举测试 ```bash ./test-election.sh ``` 该脚本会: 1. 启动 3 节点集群 2. 等待选举完成 3. 验证是否有唯一的 Leader 4. 检查任期一致性 5. 验证心跳发送频率 ### 查看日志 ```bash # 节点 1 日志 tail -f /tmp/raft-node1.log # 节点 2 日志 tail -f /tmp/raft-node2.log # 节点 3 日志 tail -f /tmp/raft-node3.log ``` ## 配置说明 关键配置项(在 `application.properties` 中): ```properties # 节点标识 raft.node.id=node-1 # 集群成员列表(逗号分隔) raft.cluster.peers=http://localhost:8081,http://localhost:8082,http://localhost:8083 # 选举超时范围(毫秒) raft.election.timeout.min=3000 raft.election.timeout.max=5000 # 心跳间隔(毫秒,建议为选举超时的 1/5 ~ 1/10) raft.heartbeat.interval=500 # 持久化数据目录 raft.data.dir=/tmp/raft-data ``` ## 设计要点 ### 选举机制 - **随机超时**:Follower 在 `[electionTimeoutMin, electionTimeoutMax]` 范围内随机选择超时时间 - **任期递增**:每次发起选举,Candidate 将 `currentTerm` 加 1 - **投票规则**:每个节点每个任期只能投一票,遵循"先到先得"原则 - **多数派原则**:获得超过半数选票即成为 Leader ### 心跳机制 - Leader 每隔 `heartbeatInterval` 向所有 Follower 发送心跳(空的 AppendEntries RPC) - Follower 收到心跳后重置选举超时,避免发起选举 - 心跳间隔应显著小于选举超时(推荐 5~10 倍关系) ### 持久化 遵循 Raft 论文要求,持久化以下状态: - `currentTerm`:当前任期号 - `votedFor`:当前任期投票给谁 节点重启后从磁盘恢复这些状态,保证选举安全性。 ### 状态转换 ``` Follower ----[选举超时]----> Candidate ----[获得多数票]----> Leader ^ | | | | | +-------[发现更高任期]--------+------------------------------+ ``` ## 与 Nacos Raft 的对比 本项目与 Nacos 中的 Raft 实现(JRaft)在思想上一致,都遵循 Raft 论文的核心原则: | 特性 | Simple Raft | Nacos (JRaft) | |------------------|--------------------------|-------------------------| | **Leader 选举** | ✅ 实现 | ✅ 实现 | | **心跳机制** | ✅ 实现 | ✅ 实现 | | **日志复制** | ❌ 未实现(预留接口) | ✅ 实现 | | **快照** | ❌ 未实现 | ✅ 实现 | | **动态成员变更** | ❌ 静态配置 | ✅ 支持 | | **PreVote** | ❌ 未实现 | ✅ 实现(优化) | | **通信协议** | HTTP REST | gRPC/Bolt | | **适用场景** | 学习、小规模测试 | 生产环境、高性能场景 | ## 后续演进计划 - [ ] **日志复制**:实现 AppendEntries 的日志同步逻辑 - [ ] **快照机制**:支持日志压缩和快照传输 - [ ] **动态成员变更**:支持集群扩缩容 - [ ] **PreVote 优化**:减少网络分区恢复时的不必要选举 - [ ] **ReadIndex 优化**:实现线性一致性读 - [ ] **性能优化**:批量处理、Pipeline - [ ] **监控指标**:暴露 Prometheus 指标 ## 常见问题 ### 1. 集群无法选出 Leader? - 检查节点间网络是否连通 - 确认配置文件中的 `peers` 列表一致 - 查看日志中是否有异常 ### 2. 节点启动后立即退出? - 检查端口是否被占用 - 确认 JDK 版本是否为 17+ - 查看日志文件中的错误信息 ### 3. 持久化状态未恢复? - 确认 `raft.data.dir` 目录有读写权限 - 检查状态文件 `{nodeId}-state.json` 是否存在 ## 贡献指南 欢迎提交 Issue 和 Pull Request! 开发建议: 1. Fork 本仓库 2. 创建特性分支 (`git checkout -b feature/your-feature`) 3. 提交更改 (`git commit -m 'Add some feature'`) 4. 推送到分支 (`git push origin feature/your-feature`) 5. 创建 Pull Request ## 参考资料 - [Raft 论文(中文翻译)](https://github.com/maemual/raft-zh_cn) - [Raft 官方网站](https://raft.github.io/) - [Raft 动画演示](http://thesecretlivesofdata.com/raft/) - [Nacos 官方文档](https://nacos.io/) ## 许可证 Apache License 2.0 ## 联系方式 如有问题或建议,欢迎通过 Issue 反馈。