基于 Raft 共识的分布式配置中心 —— 强一致 · 实时生效 · 可回滚
C++17 · braft · brpc · LevelDB · Protobuf · Docker
Configraft 是一个轻量级、强一致的分布式键值存储系统,以分布式配置中心的形态对外提供服务。它基于百度的工业级 C++ Raft 实现 braft 保证多节点数据一致,使用 LevelDB 作为单机存储引擎,并由 brpc 在同一端口提供 gRPC + HTTP 双协议接口与 Raft 内部通信。
设计目标是为微服务提供"集中管理、实时生效、可一键回滚"的配置服务,是 etcd 在 C++ 技术栈下的一个精简子集实现。
在微服务架构中,配置散落在各服务的本地文件或环境变量里,修改配置往往需要重启服务或手动分发,低效且易错。Configraft 提供:
- 集中管理:所有配置项统一存储、统一发布。
- 实时生效:配置变更后通过 Watch 长轮询立即推送到所有订阅方,零停机生效。
- 强一致:借助 Raft 共识,配置发布不因节点宕机而丢失或不一致;读取默认线性一致。
- 安全回滚:每次修改保留历史版本(MVCC),可随时一键回滚到任意历史版本。
- 演示
- 特性
- 架构
- 技术栈
- 快速开始(Docker)
- 配置参数
- 核心 API
- 一致性保证
- 目录结构
- 本地开发
- 测试
- 性能与验证
- 代码审查与安全加固
- 安全模型
- 已完成里程碑(M0–M8)
- 文档
- 参与贡献
- 致谢
- License
完整功能演示(约 3 分钟):集群状态 → KV 读写 → CAS 原子操作 → 配置发布 → Watch 实时推送 → 版本链 → 一键回滚 → HTTP API 验证
Leader 故障转移:kill Leader 后集群自动重新选举,新 Leader 接管写请求
- ✅ 基础 KV:
Put / Get / Delete / BatchPut(gRPC + HTTP) - ✅ 配置管理:多版本发布
Publish、历史查询GetConfig(key, version)、一键回滚Rollback - ✅ 实时监听:HTTP 长轮询
Watch,按全局 revision 有序推送、断点续传、慢消费者背压断开 - ✅ 原子操作:
CompareAndSwap(CAS),基于 Raft 串行 apply 天然原子 - ✅ 高可用:3 节点集群容忍 1 节点宕机,Leader 自动选举,故障数秒内恢复
- ✅ 在线扩缩容:动态
AddPeer / RemovePeer成员变更,新节点经快照追赶加入 - ✅ MVCC + Compaction:全局单调 revision 版本模型,自动回收过期版本,存储不无限膨胀
- ✅ 强一致读:Leader Lease 线性一致读,杜绝读到过期配置(braft 无 ReadIndex,采用 Lease 方案)
- ✅ 可视化管理:内置 Web Dashboard,节点状态 / 操作面板 / Watch 事件流一屏尽览
- ✅ 运维友好:健康检查、Docker 一键启动 3 节点集群、一键停止脚本
- ✅ 混沌可验证:kill Leader / 网络分区 / 节点重启故障演练脚本
Configraft 采用分层 + 多副本架构:每个节点自底向上为存储层 → 复制状态机 → Raft 层 → API 服务层,三个节点经 Raft 协议组成高可用集群,对外提供 gRPC / HTTP / Watch / Dashboard 统一接入。
- 写:客户端 → Leader API →
node->apply(task)→ 多数派确认并写日志 → commit →on_apply串行写入 LevelDB(产生新 revision 与 Watch 事件)→ 返回。 - 读(线性一致):客户端 → Leader API → 校验 Leader Lease 有效 → 等 applied ≥ commit → 从本地状态机读取 → 返回。
- Watch:客户端携带
from_revision发起长轮询 → 挂起 → 变更事件到达后立即推送 → 用新 revision 再次请求。
Leader Lease 线性一致读:braft 1.1.2 未提供 ReadIndex API,本项目利用 braft 内置 leader lease 实现线性一致读。Lease 有效 ⟹ Leader 已提交配置条目(no-op)⟹ commit 覆盖先前任期的所有已提交写 ⟹ 本地读结果与任一历史 Leader 的线性顺序一致。实现详见 docs/m5.md。
| 层级 | 选型 |
|---|---|
| 语言 | C++17 |
| 构建 | CMake + scripts/build_deps.sh(源码构建第三方依赖) |
| 一致性协议 | braft(工业级 C++ Raft,v1.1.2) |
| 网络框架 | brpc(v1.17,单端口 HTTP + gRPC + Raft 内部通信) |
| 存储引擎 | LevelDB |
| 序列化 | Protobuf3(protobuf 3.21.12) |
| 测试 | GoogleTest(store / watch / raft_cas)+ 混沌脚本 |
| 压测 | ghz(gRPC)/ wrk(HTTP),scripts/benchmark.sh |
| CI/CD | GitHub Actions(构建 + 单元测试,缓存第三方依赖) |
| 部署 | Docker / Docker Compose |
- Docker + Docker Compose v2
git clone https://github.com/ranbai7/configraft.git
cd configraft
docker compose up -d --build启动后三个节点分别暴露宿主端口 8001 / 8002 / 8003。写请求请打到 Leader(通常为 node1,可通过健康检查确认)。
# 返回节点角色 / term / commit / applied 与 leader 地址
curl http://127.0.0.1:8001/healthz
# {"message":"ok","role":"leader","term":1,"commit_index":2,"applied_index":2,"leader_id":"172.28.0.11:8001:0"}# 写入(发布配置,自动递增版本)
curl -X POST http://127.0.0.1:8001/v1/kv/gateway.rate_limit \
-d '{"value":"2000"}'
# 读取(默认线性一致)
curl http://127.0.0.1:8001/v1/kv/gateway.rate_limit
# 回滚到历史版本(写回旧值,产生新版本)
curl -X POST http://127.0.0.1:8001/v1/config/gateway.rate_limit/rollback \
-d '{"target_version":1}'# 监听全部 key 的变更(长轮询,收到变更即返回,然后携带新 revision 重新请求)
curl "http://127.0.0.1:8001/v1/watch?from_revision=0"
# 监听单个 key(/v1/watch/{key});from_revision 为续传锚点
curl "http://127.0.0.1:8001/v1/watch/gateway.rate_limit?from_revision=0"
from_revision = 0表示"从现在开始看"(不重放历史);重放历史请携带> 0的 revision 锚点。
grpcurl -proto proto/configraft.proto -plaintext 127.0.0.1:8001 \
configraft.v1.KVService/Get -d '{"key":"gateway.rate_limit"}'
# 压测(ghz)
ghz --insecure --proto proto/configraft.proto \
--call configraft.v1.KVService/Put \
-d '{"key":"k","value":"v"}' -z 10s -c 100 \
127.0.0.1:8001浏览器打开 http://127.0.0.1:8001/dashboard,可查看集群节点状态卡片、执行 KV / Config / CAS / 成员操作,并实时观察 Watch 事件流(配置变更 → 推送 → 订阅端)。详见 docs/Dashboard操作指南.md。
configraft_server 的主要启动参数(gflags):
| 参数 | 默认值 | 说明 |
|---|---|---|
--port |
8000 |
监听端口(run_cluster.sh 使用 8001–8003) |
--listen_ip |
127.0.0.1 |
监听 IP,同时作为 Raft peer 地址 |
--node |
(空) | 节点名(如 node1);留空 = 单机模式(无 Raft) |
--peers |
(空) | 集群节点列表,逗号分隔:ip:port,ip:port;留空 = 单机模式 |
--data_dir |
data |
LevelDB 数据目录 |
--election_timeout_ms |
1000 |
Raft 选举超时(毫秒) |
--web_dir |
web |
Dashboard 静态资源目录 |
--raft_sync |
true |
写日志是否 fsync 持久化;设 false 可显著提升写吞吐(压测/演示用,牺牲崩溃持久性) |
单机模式示例:
./build/configraft_server --port 8100 --data_dir /tmp/cfg-m1;集群启动脚本见 scripts/run_cluster.sh。
| 方法 | HTTP 路径 | 说明 |
|---|---|---|
Put(key, value) |
POST /v1/kv/{key} |
写入(body: {"value":"..."}) |
Get(key) |
GET /v1/kv/{key} |
读取,默认线性一致;?serializable=true 允许 Follower 读 |
Delete(key) |
DELETE /v1/kv/{key} |
删除(写 tombstone) |
BatchPut(entries) |
POST /v1/kv/batch |
批量写入(body: {"entries":[{"key","value"},...]}) |
CompareAndSwap(key, expect, value) |
POST /v1/kv/{key}/cas |
条件更新(body: {"expect","value"}),原子 |
| 方法 | HTTP 路径 | 说明 |
|---|---|---|
Publish(key, value) |
POST /v1/config/{key} |
发布新版本 |
GetConfig(key, version) |
GET /v1/config/{key}?version=N |
读指定版本;version 缺省/≤0 为最新 |
Rollback(key, target_version) |
POST /v1/config/{key}/rollback |
一键回滚(写回旧值,产生新版本) |
| 方法 | HTTP 路径 | 说明 |
|---|---|---|
Watch(key, from_revision) |
GET /v1/watch(全部)/ GET /v1/watch/{key} |
长轮询变更推送,断点续传 |
GetHealth() |
GET /healthz |
节点角色 / term / commit / applied |
AddPeer(peer) |
POST /addpeer |
在线加节点(body: {"peer":"ip:port"}) |
RemovePeer(peer) |
POST /removepeer |
在线移除节点 |
错误码:
0OK ·1KEY_NOT_FOUND ·2CAS_FAILED ·3NOT_LEADER(响应含 leader 地址,客户端应重定向)·4VERSION_NOT_FOUND ·5COMPACTED(历史已回收,用 current_revision 续传)·6INVALID_ARGUMENT ·100INTERNAL。gRPC 方法路径:
configraft.v1.KVService/{Put,Get,Delete,BatchPut,CompareAndSwap}、configraft.v1.ConfigService/{Publish,GetConfig,Rollback}、configraft.v1.WatchService/Watch。AdminService 三方法(GetHealth/AddPeer/RemovePeer)因 brpc restful 映射占用 gRPC 路径,仅提供 HTTP(见 docs/m6.md)。
| 操作 | 一致性 | 实现 |
|---|---|---|
| 所有写操作 | 线性一致 | Raft 多数派确认后 on_apply 串行落库 |
Get(默认) |
线性一致 | Leader Lease 校验 + applied ≥ commit 后本地读 |
Get(serializable=true) |
可容忍轻微过期 | Follower 本地读,用于监控 |
Watch |
有序、不丢、可续传 | 事件来自 Raft apply 序列,按全局 revision 排序;历史回收时返回 COMPACTED + 新锚点 |
configraft/
├── CMakeLists.txt
├── Dockerfile / docker-compose.yml # 3 节点集群一键启动
├── proto/configraft.proto # gRPC / Protobuf 协议定义
├── src/
│ ├── server/ # brpc 服务(KV/Config/Watch/Admin/Dashboard + CORS)
│ ├── raft/ # ConfigNode 抽象、RaftNode/LocalNode、复制状态机、快照
│ ├── store/ # LevelDB 封装、MVCC、Compaction、快照导入导出
│ ├── watch/ # WatchHub 长轮询分发(订阅/重放/背压)
│ └── common/ # storekey 编码、状态码、日志
├── client/ # 示例客户端(client_main.cpp)
├── scripts/ # build_deps / run_cluster / chaos_test / benchmark
├── tests/ # GoogleTest(store / watch / raft_cas)
├── web/ # Dashboard 前端静态资源(原生 JS)
└── docs/ # 架构 / 里程碑 / 压测 / 安全审查等文档
bash scripts/build_deps.sh脚本幂等构建到 third_party/install(约 148MB),含项目定制补丁(braft 构建修复等)。
cmake -B build
cmake --build build -j4
ctest --test-dir build # 4 个测试:store / watch / raft_cas / http(HTTP 路由集成)# 单机模式(LocalNode,无 Raft,用于功能调试)
./build/configraft_server --port 8100 --data_dir /tmp/cfg-m1
# 3 节点集群(端口 8001-8003)
bash scripts/run_cluster.sh
bash scripts/run_cluster.sh stop # 一条命令停止集群单机模式不启动 Raft,可快速验证 KV/Config/Watch 全部功能;集群模式才提供一致性保证与故障转移。
测试代码见 tests/,混沌演练脚本见 scripts/chaos_test.sh。
- 单元测试(GoogleTest):store(MVCC 语义)、watch(事件有序 / 背压)、raft_cas(CAS 并发原子性)。
- HTTP 集成测试:真实拉起 server,验证 RESTful 路由分发、CORS 收紧、参数校验、错误码、内置服务关闭与 gRPC 共存。
- 混沌测试:kill Leader / kill Follower / 网络分区 / 节点重启,验证写线性一致、读一致、Watch 有序不丢、集群自动恢复。
- 性能回归:压测矩阵与复现方式见「性能与验证」及 docs/性能压测报告.md。
ctest --test-dir build # 4 个测试:store / watch / raft_cas / http
bash scripts/chaos_test.sh # 混沌演练测试环境:阿里云云主机 · 8 vCPU / 16G 内存 / NVMe SSD · Ubuntu 22.04 · 多节点进程同机(本地环回)· ghz(gRPC)/ wrk(HTTP)· 每项 10s。
指标格式:
QPS / P99(ms)——例28.7k / 0.6= 28.7k QPS、P99 0.6ms;gRPC 按 ×8 / ×64 / ×128 并发,HTTP 固定 100 连接。
| 场景 | 单机 | 3 节点 · 默认持久化 | 3 节点 · 关 fsync | 5 节点 · 默认持久化 | 5 节点 · 关 fsync |
|---|---|---|---|---|---|
| gRPC 写 Put ×8 | 28.7k / 0.6 | 1.8k / 10.3 | 15.9k / 1.4 | 1.2k / 11.0 | 13.0k / 1.7 |
| gRPC 写 Put ×64 | 41.8k / 3.1 | 10.0k / 11.6 | 28.2k / 4.7 | 7.4k / 14.7 | 24.4k / 5.8 |
| gRPC 写 Put ×128 | 38.1k / 6.1 | 16.0k / 13.0 | 31.0k / 7.1 | 12.6k / 18.9 | 27.3k / 8.8 |
| gRPC 读 Get ×64 | 38.6k / 3.6 | 45.2k / 2.8 | 42.5k / 3.3 | 44.1k / 3.0 | 40.5k / 4.1 |
| HTTP 读 GET | 259k / 0.4† | 275k / 0.5 | 271k / 0.5 | 267k / 0.5 | 263k / 0.5 |
| HTTP 写 POST | 102k / 47† | 17.9k / 12.6 | 75.4k / 4.2 | 13.0k / 15.5 | 50.4k / 1330† |
① 数据为 2026-08-20 阿里云实测(8 vCPU / 16G / NVMe SSD / Ubuntu 22.04,多节点进程同机环回),为审查加固后(commit e5d737b 加锁)当前代码实测;加锁前后同环境对比无一项回退——差异多为 ±5% 内,最大单格 +7%(
HTTP 写 POST·关 fsync,且是变快不是变慢)(详见 docs/性能压测报告.md §6)。 ②†单机 fsync=true 组 / 5 节点关 fsync 组的 HTTP P99 受同机压测 CPU 资源竞争影响产生长尾噪音(读路径与 fsync 无关),QPS 仍稳定。
关键结论:
- 读远高于写:HTTP 读 25–30 万 QPS vs 写 1.2k–31k——符合配置中心"读多写少"业务形态。
- Raft 复制规模代价:5 节点写吞吐比 3 节点低 15–32%(多数派 2/3 → 3/5),读路径几乎不受影响(Leader Lease 本地读);代价换来容错升级(3 节点容忍 1 故障、5 节点容忍 2 故障)。
- fsync 是写路径最大开销:默认持久化 vs 关 fsync,低并发(c8)下相差 8–10 倍(3 节点 1.8k → 15.9k);并发升高后差距收窄(×64 约 2.8 倍、×128 约 1.9 倍)——"强一致 + 崩溃不丢已提交日志"的固有代价(
--raft_sync=false仅压测/演示用)。 - 写路径峰值(8 核 CPU 上限):单机约 38k、3 节点关 fsync 约 31k、5 节点关 fsync 约 27k QPS(c64→c128 已趋饱和)。
审查加固后复测(2026-08-20,锁开销验证):安全审查(commit e5d737b)引入 Store 共享互斥量与单机写锁后,在同环境(8 核阿里云)复测完整矩阵:集群写路径无一项回退(差异多为 ±5% 内,最大单格 +7%——HTTP 写 POST·关 fsync 从 70.7k 升到 75.4k,是变快不是变慢),读路径完全不受影响——共享锁修复快照 UAF 的加固零性能代价。对比表与原始证据(环境指纹 + 二进制 md5 + 逐项原始输出)见 docs/性能压测报告.md §6。
完整数据与复现方式见 docs/性能压测报告.md;
bash scripts/benchmark.sh --all --dur 10可一键复跑完整矩阵。
2026-08-20 完成一次全项目代码审查(Raft 核心 / 服务层 / 存储 / 前端 4 个维度并行),发现并修复 9 项问题(commit e5d737b),主要包括:
- 快照并发 UAF(HIGH):
LoadSnapshot重建 LevelDB 时与并发读/Compaction 无同步 → 引入共享互斥量保护存储生命周期 - 单机模式写并发(HIGH):
LocalNode并发写导致全局 revision 重复 → 写路径串行化 - 通配 CORS(HIGH):允许任意网页跨站读写 → 收紧为仅同主机跨端口放行
- 快照 revision 原子性、快照含配置版本索引、层级 key 历史碰撞、写失败原子提交、key 校验、关闭 brpc 内置服务面板等
完整发现清单(含未修项与设计取舍)见 docs/安全审查报告.md。
当前版本未内置鉴权,面向受信任内网 / 单机演示场景。请勿将端口直接暴露到不可信网络;生产部署请在网关注入鉴权或做网络隔离。
已采取的缓解措施:
- CORS 收紧:
Access-Control-Allow-Origin仅回显同主机来源,阻断恶意网页跨站读写(配置 / 成员 / 数据)。 - 关闭内置服务面板:brpc
/status/vars等内部监控页已禁用,避免泄露运行参数。 - 参数校验:拒绝空 key(
INVALID_ARGUMENT)。
| 里程碑 | 内容 | 状态 |
|---|---|---|
| M0 | 项目骨架:CMake / 单元测试框架 / 代码规范 | ✅ |
| M1 | 单机 KV:brpc + LevelDB + Protobuf(LocalNode) |
✅ |
| M2 | Raft 接入:braft 3 节点写入一致 + 自动选主 | ✅ |
| M3 | MVCC 版本模型:Publish / GetConfig / Rollback + Compaction |
✅ |
| M4 | Watch 长轮询 + revision 断点续传 + 背压 | ✅ |
| M5 | CAS 原子操作 + Leader Lease 线性一致读 | ✅ |
| M6 | 成员变更(AddPeer/RemovePeer)+ Docker 一键集群 + 健康检查 | ✅ |
| M7 | 性能优化(bthread 原语修复、fsync 热点分析)+ 混沌测试 + 压测 | ✅ |
| M8 | 全项目代码审查与安全加固(9 项修复) | ✅ |
后续方向:接口鉴权(令牌 / IP 白名单)、HTTP 路由层集成测试、apply 幂等(崩溃重放防护)、快照安装与读并发压测。
深入阅读项目的架构、设计与验证材料:
| 分类 | 文档 | 内容 |
|---|---|---|
| 设计 | 项目概述.md | 项目定位、架构决策与设计取舍 |
| 设计 | 开发计划.md | M0–M8 里程碑规划与验收标准 |
| 设计 | raft-and-braft.md | Raft 原理 + braft 源码分析 + Configraft 落地 |
| 设计 | LevelDB.md | LSM-Tree 存储引擎原理 |
| 设计 | watch.md | Watch 长轮询与断点续传设计 |
| 设计 | m5.md | M5:CAS 原子更新 + Leader Lease 线性一致读 |
| 设计 | m6.md | M6:成员变更 + 健康检查 + Docker 集群 |
| 设计 | m7.md | M7:性能优化 + 混沌压测 |
| 验证 | 性能压测报告.md | 2026-08 云主机实测完整数据与结论 |
| 验证 | 安全审查报告.md | 全项目审查:发现、修复与未修取舍 |
| 运维 | Dashboard操作指南.md | Dashboard 逐功能操作手册 |
| 运维 | 演示脚本.md | 业务视角完整演示流程 |
欢迎提交 Issue / PR。构建步骤、开发流程、提交规范与代码风格见 CONTRIBUTING.md;常见设计决策与使用问题见 FAQ.md。
如果 Configraft 对你有帮助,欢迎点个 ⭐。

