5Draft(五帝)使用说明书
版本:0.1.0 | 更新日期:2026-09-05 | 适用对象:将 5Draft 集成到现有 Raft 集群的开发者与运维人员
项目地址:https://gitee.com/galaxy_0/5d-raft.git
本说明书聚焦于如何使用 5Draft,涵盖快速上手、API 参考、配置调优、集成就绪、验证与运维排障。项目背景与设计动机见 README.md,完整方案见 doc/方案.md。
目录
1. 快速上手
1.1 获取源码
bash
git clone --recursive https://gitee.com/galaxy_0/5d-raft.git
cd 5d-raft
# 若克隆时漏了子模块:
git submodule update --init --recursive
1.2 安装依赖
| 平台 | 命令 |
|---|---|
| Debian / Ubuntu | sudo apt-get install -y libasio-dev zlib1g-dev cmake g++ |
| RHEL / CentOS | sudo dnf install -y asio-devel zlib-devel cmake gcc-c++ |
| Windows (vcpkg) | vcpkg install asio zlib |
WSL 用户:直接使用 Linux 命令即可,推荐 WSL2。
1.3 构建
bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
构建产物位于 build/:
| 产物 | 用途 |
|---|---|
libdraft5.a |
核心库(健康采集/趋势/调度/投票/消息扩展) |
libdraft5_nuraft.a |
NuRaft 集成库(含真实节点封装) |
5draft_demo |
仿真集群三场景演示 |
nuraft_demo |
真实 NuRaft 集群 + 混沌注入演示 |
nuraft_real_test |
全链路真实验证(真实 CPU 压力) |
unit_tests |
31 项单元测试 |
1.4 一键验证
bash
ctest --test-dir build --output-on-failure # 单元测试
./build/5draft_demo # 仿真闭环
./build/nuraft_real_test # 全链路真实禅让验证
2. 核心概念
2.1 健康快照 HealthSnapshot
cpp
struct HealthSnapshot {
double h; // 静态健康分 H(t) ∈ [0,1]
double h1; // 一阶导数 dH/dt(变化速率,单位:h/心跳周期)
double h2; // 二阶导数 d²H/dt²(变化加速度)
double pred; // 预测性健康分 H_pred ∈ [0,1]
uint64_t timestamp_ms; // 采样时间戳
};
预测公式(泰勒展开外推 4 个心跳周期):
H_pred = clamp(H + α·H' + 0.5·β·H'', 0, 1)
其中 α = 4.0, β = 0.5
- H′ < 0:健康正在下滑
- H″ > 0:下滑在减速(触底反弹),瞬态抖动特征
- H″ < 0:下滑在加速,持续劣化特征
2.2 主动禅让判定流程
#mermaid-svg-ZxE0yECyW1stjeKu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZxE0yECyW1stjeKu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZxE0yECyW1stjeKu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZxE0yECyW1stjeKu .error-icon{fill:#552222;}#mermaid-svg-ZxE0yECyW1stjeKu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZxE0yECyW1stjeKu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZxE0yECyW1stjeKu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZxE0yECyW1stjeKu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZxE0yECyW1stjeKu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZxE0yECyW1stjeKu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZxE0yECyW1stjeKu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZxE0yECyW1stjeKu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZxE0yECyW1stjeKu .marker.cross{stroke:#333333;}#mermaid-svg-ZxE0yECyW1stjeKu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZxE0yECyW1stjeKu p{margin:0;}#mermaid-svg-ZxE0yECyW1stjeKu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZxE0yECyW1stjeKu .cluster-label text{fill:#333;}#mermaid-svg-ZxE0yECyW1stjeKu .cluster-label span{color:#333;}#mermaid-svg-ZxE0yECyW1stjeKu .cluster-label span p{background-color:transparent;}#mermaid-svg-ZxE0yECyW1stjeKu .label text,#mermaid-svg-ZxE0yECyW1stjeKu span{fill:#333;color:#333;}#mermaid-svg-ZxE0yECyW1stjeKu .node rect,#mermaid-svg-ZxE0yECyW1stjeKu .node circle,#mermaid-svg-ZxE0yECyW1stjeKu .node ellipse,#mermaid-svg-ZxE0yECyW1stjeKu .node polygon,#mermaid-svg-ZxE0yECyW1stjeKu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZxE0yECyW1stjeKu .rough-node .label text,#mermaid-svg-ZxE0yECyW1stjeKu .node .label text,#mermaid-svg-ZxE0yECyW1stjeKu .image-shape .label,#mermaid-svg-ZxE0yECyW1stjeKu .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZxE0yECyW1stjeKu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZxE0yECyW1stjeKu .rough-node .label,#mermaid-svg-ZxE0yECyW1stjeKu .node .label,#mermaid-svg-ZxE0yECyW1stjeKu .image-shape .label,#mermaid-svg-ZxE0yECyW1stjeKu .icon-shape .label{text-align:center;}#mermaid-svg-ZxE0yECyW1stjeKu .node.clickable{cursor:pointer;}#mermaid-svg-ZxE0yECyW1stjeKu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZxE0yECyW1stjeKu .arrowheadPath{fill:#333333;}#mermaid-svg-ZxE0yECyW1stjeKu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZxE0yECyW1stjeKu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZxE0yECyW1stjeKu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZxE0yECyW1stjeKu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZxE0yECyW1stjeKu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZxE0yECyW1stjeKu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZxE0yECyW1stjeKu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZxE0yECyW1stjeKu .cluster text{fill:#333;}#mermaid-svg-ZxE0yECyW1stjeKu .cluster span{color:#333;}#mermaid-svg-ZxE0yECyW1stjeKu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZxE0yECyW1stjeKu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZxE0yECyW1stjeKu rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZxE0yECyW1stjeKu .icon-shape,#mermaid-svg-ZxE0yECyW1stjeKu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZxE0yECyW1stjeKu .icon-shape p,#mermaid-svg-ZxE0yECyW1stjeKu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZxE0yECyW1stjeKu .icon-shape .label rect,#mermaid-svg-ZxE0yECyW1stjeKu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZxE0yECyW1stjeKu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZxE0yECyW1stjeKu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZxE0yECyW1stjeKu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
否
是
Leader tick
Follower 预测分 - 自身预测分 > threshold?
重置连续计数
连续计数 +1
计数 >= consecutive_required?
等待下一周期
yield_leadership false, successor
NuRaft 暂停写入 → 继任者追平 → 优雅退位
2.3 健康感知投票
| 条件 | 行为 |
|---|---|
| Leader Lease 失效(超过 300ms 未收到 Leader 心跳) | 回退标准 Raft,授票 |
| Lease 有效 + 候选人预测分 > Leader 预测分 + threshold | 授票 |
| Lease 有效 + 候选人不显著更优 | 拒绝(由 NuRaft 原生日志完整性门禁最终决定) |
⚠️ 5Draft 的投票决策是观测与辅助,实际授票动作由 NuRaft 原生逻辑完成(含日志完整性门禁),不会破坏 Raft Safety。
3. API 参考
3.1 NuraftNode(推荐入口)
真实 NuRaft 节点的完整封装,开箱即用。
cpp
#include "draft5/nuraft/nuraft_node.hpp"
using namespace draft5::nuraft_support;
NuraftNode::Options opts;
opts.id = 1;
opts.host = "127.0.0.1";
opts.port = 25000;
opts.weights = HealthWeights::LatencySensitive();
opts.health_period_ms = 100; // 健康采集间隔(≈心跳间隔)
opts.tick_period_ms = 50; // Leader 择优检查间隔
opts.sched.threshold = 0.08;
opts.sched.consecutive_required = 3;
NuraftNode node(opts);
node.Init(); // 启动健康采集、meta 回调、Leader tick 线程
node.AddPeer(2, "127.0.0.1:25001"); // Leader 侧加入成员
// ...
node.Shutdown();
Options 字段:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
id |
int | 1 | 节点 ID(≥1) |
host |
string | "127.0.0.1" | 监听地址 |
port |
int | 25000 | 监听端口 |
weights |
HealthWeights | LatencySensitive | 健康权重 |
sched |
ActiveScheduler::Config | 默认 | 调度器参数 |
health_period_ms |
int | 100 | 健康采集间隔(ms) |
tick_period_ms |
int | 50 | Leader tick 间隔(ms) |
常用方法:
| 方法 | 说明 |
|---|---|
Init() |
初始化并启动所有后台线程 |
AddPeer(id, endpoint) |
Leader 侧串行加入成员 |
PeerCount() |
当前集群成员数 |
leader_id() |
当前 Leader ID(-1 表示无) |
term() |
当前任期 |
is_leader() |
本节点是否为 Leader |
Shutdown() |
优雅关闭 |
draft() |
获取底层 FiveDraftNode,用于注入混沌演练等 |
3.2 FiveDraftNode(核心集成层)
若不使用 NuraftNode,可直接用 FiveDraftNode 配合自己的 Raft 适配层。
cpp
#include "draft5/five_draft_node.hpp"
FiveDraftNode node(1, HealthWeights::ComputeIntensive(), sched_config);
node.Start(100); // 启动健康采集
// 在你的 Raft 心跳/投票 meta 回调中调用:
std::string meta = node.WriteResponseMeta(); // 出站:写入自身健康
node.ReadResponseMeta(peer_id, meta); // 入站:解析对端健康
// Leader 周期性调用:
node.LeaderTick(my_raft_server_ptr);
// 混沌演练(仅测试/演示):
HealthSnapshot fake;
fake.h = 0.35; fake.h1 = -0.05;
fake.pred = ComputePrediction(fake.h, fake.h1, fake.h2);
node.BeginDrill(fake);
// ... 观察禅让 ...
node.EndDrill();
消息扩展回调四件套(对应 NuRaft asio_service 的 4 个 meta 钩子):
| 方法 | 调用时机 | 作用 |
|---|---|---|
WriteResponseMeta() |
发心跳/投票响应前 | 写入本节点健康快照 |
ReadResponseMeta(peer_id, meta) |
Leader 收到 Follower 响应 | 更新 Follower 健康视图 |
WriteRequestMeta() |
发心跳/投票请求前 | 写入本节点健康快照 |
ReadRequestMeta(src_id, type, meta) |
收到心跳/投票请求 | 缓存 Leader 健康 / 健康感知投票判定 |
3.3 HealthMonitor(健康采集)
cpp
HealthMonitor monitor(HealthWeights{0.5, 0.2, 0.3});
monitor.Start(100);
// 注册业务线程(真实 CPU 记账的关键!)
// 必须在被统计的线程内部调用:
uint64_t tid = monitor.RegisterCpuThread();
// ... 线程工作 ...
monitor.UnregisterCpuThread(tid); // 线程退出前
// 上报网络 RTT(由你的网络层调用):
monitor.UpdateRttScore(rtt_ms);
// 读取健康快照:
HealthSnapshot snap = monitor.GetSnapshot();
double cpu = monitor.LastCpuAvailable();
权重模板:
| 模板 | CPU | 内存 | 网络 | 适用场景 |
|---|---|---|---|---|
ComputeIntensive() |
0.5 | 0.2 | 0.3 | 计算密集型服务 |
MemoryIntensive() |
0.2 | 0.5 | 0.3 | 内存密集型服务 |
LatencySensitive() |
0.2 | 0.2 | 0.6 | 延迟敏感型服务 |
3.4 ActiveScheduler(主动调度器)
cpp
ActiveScheduler::Config cfg;
cfg.threshold = 0.08; // 预测分差值阈值
cfg.consecutive_required = 3; // 连续优于次数
cfg.check_interval_ms = 50; // 检查限频
cfg.stale_ms = 500; // 健康数据过期时间
ActiveScheduler scheduler(cfg);
scheduler.UpdatePeerHealth(peer_id, wire); // 收到 Follower 健康
scheduler.OnLeaderTick(raft_ptr, self_snap); // Leader tick
3.5 HealthWeights 权重自定义
cpp
HealthWeights w{0.4, 0.3, 0.3}; // CPU / 内存 / 网络,三者之和建议为 1.0
4. 配置指南
4.1 调度器参数 ActiveScheduler::Config
| 参数 | 默认值 | 建议范围 | 说明 |
|---|---|---|---|
threshold |
0.08 | 0.05 ~ 0.15 | 预测分差值阈值;过小易误切换,过大反应迟钝 |
consecutive_required |
3 | 2 ~ 5 | 连续优于次数;防抖核心参数 |
check_interval_ms |
50 | 20 ~ 200 | Leader 检查间隔,建议 ≤ 心跳间隔的一半 |
stale_ms |
500 | 300 ~ 1000 | 超过该时长未上报的健康数据视为过期 |
4.2 预测参数 health_types.hpp
| 参数 | 默认值 | 说明 |
|---|---|---|
kPredAlpha |
4.0 | 外推心跳周期数(越大看得越远) |
kPredBeta |
0.5 | 二阶修正系数 |
kWindowSize |
6 | 趋势分析滑动窗口采样点数 |
4.3 调优方法论
- 干跑观察 :先部署但禁用禅让(将
consecutive_required设为极大值),只打日志,观察健康分波动范围。 - 取 3σ 定阈值 :统计基线期健康分标准差 σ,将
threshold设为约 3σ(实测约 0.08)。 - 调防抖 :若出现误切换,增大
consecutive_required或threshold。 - 调灵敏度 :若反应太慢,减小
consecutive_required或check_interval_ms。
4.4 权重选择决策树
#mermaid-svg-OliEQ4QuzUOhqEHv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OliEQ4QuzUOhqEHv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OliEQ4QuzUOhqEHv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OliEQ4QuzUOhqEHv .error-icon{fill:#552222;}#mermaid-svg-OliEQ4QuzUOhqEHv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OliEQ4QuzUOhqEHv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OliEQ4QuzUOhqEHv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OliEQ4QuzUOhqEHv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OliEQ4QuzUOhqEHv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OliEQ4QuzUOhqEHv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OliEQ4QuzUOhqEHv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OliEQ4QuzUOhqEHv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OliEQ4QuzUOhqEHv .marker.cross{stroke:#333333;}#mermaid-svg-OliEQ4QuzUOhqEHv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OliEQ4QuzUOhqEHv p{margin:0;}#mermaid-svg-OliEQ4QuzUOhqEHv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OliEQ4QuzUOhqEHv .cluster-label text{fill:#333;}#mermaid-svg-OliEQ4QuzUOhqEHv .cluster-label span{color:#333;}#mermaid-svg-OliEQ4QuzUOhqEHv .cluster-label span p{background-color:transparent;}#mermaid-svg-OliEQ4QuzUOhqEHv .label text,#mermaid-svg-OliEQ4QuzUOhqEHv span{fill:#333;color:#333;}#mermaid-svg-OliEQ4QuzUOhqEHv .node rect,#mermaid-svg-OliEQ4QuzUOhqEHv .node circle,#mermaid-svg-OliEQ4QuzUOhqEHv .node ellipse,#mermaid-svg-OliEQ4QuzUOhqEHv .node polygon,#mermaid-svg-OliEQ4QuzUOhqEHv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OliEQ4QuzUOhqEHv .rough-node .label text,#mermaid-svg-OliEQ4QuzUOhqEHv .node .label text,#mermaid-svg-OliEQ4QuzUOhqEHv .image-shape .label,#mermaid-svg-OliEQ4QuzUOhqEHv .icon-shape .label{text-anchor:middle;}#mermaid-svg-OliEQ4QuzUOhqEHv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OliEQ4QuzUOhqEHv .rough-node .label,#mermaid-svg-OliEQ4QuzUOhqEHv .node .label,#mermaid-svg-OliEQ4QuzUOhqEHv .image-shape .label,#mermaid-svg-OliEQ4QuzUOhqEHv .icon-shape .label{text-align:center;}#mermaid-svg-OliEQ4QuzUOhqEHv .node.clickable{cursor:pointer;}#mermaid-svg-OliEQ4QuzUOhqEHv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OliEQ4QuzUOhqEHv .arrowheadPath{fill:#333333;}#mermaid-svg-OliEQ4QuzUOhqEHv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OliEQ4QuzUOhqEHv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OliEQ4QuzUOhqEHv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OliEQ4QuzUOhqEHv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OliEQ4QuzUOhqEHv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OliEQ4QuzUOhqEHv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OliEQ4QuzUOhqEHv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OliEQ4QuzUOhqEHv .cluster text{fill:#333;}#mermaid-svg-OliEQ4QuzUOhqEHv .cluster span{color:#333;}#mermaid-svg-OliEQ4QuzUOhqEHv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OliEQ4QuzUOhqEHv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OliEQ4QuzUOhqEHv rect.text{fill:none;stroke-width:0;}#mermaid-svg-OliEQ4QuzUOhqEHv .icon-shape,#mermaid-svg-OliEQ4QuzUOhqEHv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OliEQ4QuzUOhqEHv .icon-shape p,#mermaid-svg-OliEQ4QuzUOhqEHv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OliEQ4QuzUOhqEHv .icon-shape .label rect,#mermaid-svg-OliEQ4QuzUOhqEHv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OliEQ4QuzUOhqEHv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OliEQ4QuzUOhqEHv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OliEQ4QuzUOhqEHv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} CPU 密集
内存密集
延迟敏感
混合/不确定
你的服务类型?
ComputeIntensive 0.5/0.2/0.3
MemoryIntensive 0.2/0.5/0.3
LatencySensitive 0.2/0.2/0.6
默认 0.3/0.3/0.4,干跑后调整
5. 集成到现有 NuRaft 工程
5.1 链接库
cmake
add_subdirectory(path/to/5dRaft)
target_link_libraries(your_app PRIVATE draft5_nuraft)
5.2 替换你的节点启动代码
将原有 raft_launcher 启动代码替换为 NuraftNode,5Draft 会自动:
- 安装 4 个 asio meta 回调(健康数据随心跳携带)
- 启动健康采集线程
- 启动 Leader tick 线程(择优 + 禅让)
cpp
draft5::nuraft_support::NuraftNode::Options opts;
opts.id = my_id;
opts.host = my_host;
opts.port = my_port;
// ... 你的 NuRaft 参数 ...
draft5::nuraft_support::NuraftNode node(opts);
node.Init();
5.3 注册业务线程(重要)
为了让 CPU 健康采集反映真实业务负载,将你的业务线程池线程注册到健康监控器:
cpp
// 在你的业务线程函数开头:
uint64_t tid = node.draft().Monitor().RegisterCpuThread();
// ... 业务逻辑 ...
// 在线程退出前:
node.draft().Monitor().UnregisterCpuThread(tid);
未注册线程时,CPU 可用率恒为 1.0(满余),不会触发 CPU 维度的禅让。
5.4 上报网络 RTT
在你的网络层测量到对端 RTT 后上报:
cpp
node.draft().Monitor().UpdateRttScore(measured_rtt_ms);
RTT 评分规则:score = clamp(1 - rtt_ms / 100, 0, 1),即 0ms=1.0,100ms=0.0。
6. 运行与验证
6.1 单元测试
bash
./build/unit_tests
输出 N checks, 0 failures 表示通过。覆盖:趋势分析、消息编解码(含边界)、主动调度、投票决策、HealthMonitor(RTT/Reset/CPU 记账)、仿真端到端。
6.2 仿真演示(无网络依赖)
bash
./build/5draft_demo
三个场景:
- 持续劣化 → 主动禅让
- 瞬态抖动 → 二阶导 + 连续计数抑制,不切换
- 投票决策 → Lease 有效择优 / 失效回退
6.3 真实集群演示(混沌注入)
bash
./build/nuraft_demo
启动 3 个真实 NuRaft 节点(端口 25011~25013),对 Leader 注入劣化健康,观察禅让。
6.4 全链路真实验证(推荐)
bash
./build/nuraft_real_test
无任何 Mock :真实 3 节点集群 + 对 Leader 施加 nproc 线程真实 CPU 压力,验证:
| 检查项 | 通过标准 |
|---|---|
| 集群组阁 | Leader 选出,term=1 |
| 基线健康 | CPU 可用率 > 0.9,H > 0.75 |
| 施压降载 | CPU 可用率 < 0.5 |
| 预测下滑 | pred 较基线下降 > 0.15 |
| 主动禅让 | Leader 切换,term 递增 |
| 卸载恢复 | CPU 可用率恢复 > 0.9 |
7. 运维与调优
7.1 日志级别
cpp
#include "draft5/logger.hpp"
draft5::SetLogLevel(draft5::LogLevel::Debug); // Debug/Info/Warn/Error
关键日志关键字(便于 grep 定位):
| 关键字 | 含义 |
|---|---|
5Draft: Leader N 主动禅让给 M |
触发主动禅让,含 pred diff 与 term |
5Draft Vote: |
健康感知投票决策 |
5Draft node N 进入混沌演练 |
混沌演练开始 |
7.2 观测节点健康
cpp
HealthSnapshot s = node.draft().Monitor().GetSnapshot();
double cpu = node.draft().Monitor().LastCpuAvailable();
double mem = node.draft().Monitor().LastMemoryAvailable();
// s.h, s.h1, s.h2, s.pred
7.3 常见异常与对策
| 现象 | 可能原因 | 对策 |
|---|---|---|
| 禅让未触发 | 阈值过大 / 连续次数过高 / 健康分差不足 | 降低 threshold 或 consecutive_required;检查健康数据是否随心跳交换 |
| 频繁切换(乒乓) | 阈值过小 / 防抖不足 | 增大 threshold 或 consecutive_required |
| 健康分恒为 0.93 | 未注册业务线程 / 未上报 RTT | 调用 RegisterCpuThread / UpdateRttScore |
| 禅让后旧 Leader 健康仍低 | 真实负载未消除 | 检查业务负载是否随角色迁移 |
| 编译找不到 NuRaft 头 | 子模块未拉取 | git submodule update --init --recursive |
7.4 优雅关闭
cpp
node.Shutdown(); // 内部停止健康采集线程、Leader tick 线程、NuRaft 服务
7.5 动态调整配置
cpp
ActiveScheduler::Config new_cfg;
new_cfg.threshold = 0.10;
node.draft().Scheduler().SetConfig(new_cfg); // 线程安全
8. 常见问题(FAQ)
Q1:5Draft 会破坏 Raft 的安全性吗?
不会。日志完整性投票门禁由 NuRaft 原生保证,5Draft 不绕过任何 Safety 检查。禅让走 NuRaft 原生 yield_leadership 路径,Lease 失效时自动回退标准 Raft。
Q2:健康数据在网络上怎么传?会增加带宽吗?
健康快照 (h, h1, h2) 编码为 "0.93,-0.01,0.001" 形式的字符串,随心跳 meta 通道捎带,不新增 RPC。每条消息增加约 20~30 字节,可忽略。
Q3:为什么用线程级 CPU 记账而不是 /proc/stat?
容器环境下 /proc/stat 反映的是宿主机而非容器的 CPU,不可信。线程级记账(/proc/self/task/<tid>/stat)精确统计注册线程的真实 CPU 消耗,且支持单机多节点共进程场景。
Q4:如何在生产环境灰度上线?
- 先部署 5Draft 但设
consecutive_required = INT_MAX(只采集不禅让); - 观察 1~2 周健康分日志,确定合理
threshold; - 逐步恢复
consecutive_required默认值,观察禅让行为; - 全量放开。
Q5:支持 Windows 吗?
支持。Windows 下 CPU 采集用 GetThreadTimes,内存用 GlobalMemoryStatusEx。NuRaft/asio 官方支持 Windows。
Q6:如何贡献代码?
提交 Issue 或 PR 到 https://gitee.com/galaxy_0/5d-raft 。新增平台健康采集(如 macOS)欢迎贡献。
附录:协议
本项目原创代码 MIT License。第三方组件协议:NuRaft (Apache-2.0)、asio (BSL-1.0)、zlib (zlib)。详见 README.md 开源协议章节。