5Draft使用说明书

5Draft(五帝)使用说明书

版本:0.1.0 | 更新日期:2026-09-05 | 适用对象:将 5Draft 集成到现有 Raft 集群的开发者与运维人员
项目地址:https://gitee.com/galaxy_0/5d-raft.git

本说明书聚焦于如何使用 5Draft,涵盖快速上手、API 参考、配置调优、集成就绪、验证与运维排障。项目背景与设计动机见 README.md,完整方案见 doc/方案.md


目录

  1. 快速上手
  2. 核心概念
  3. [API 参考](#API 参考)
  4. 配置指南
  5. [集成到现有 NuRaft 工程](#集成到现有 NuRaft 工程)
  6. 运行与验证
  7. 运维与调优
  8. 常见问题(FAQ)

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 调优方法论

  1. 干跑观察 :先部署但禁用禅让(将 consecutive_required 设为极大值),只打日志,观察健康分波动范围。
  2. 取 3σ 定阈值 :统计基线期健康分标准差 σ,将 threshold 设为约 3σ(实测约 0.08)。
  3. 调防抖 :若出现误切换,增大 consecutive_requiredthreshold
  4. 调灵敏度 :若反应太慢,减小 consecutive_requiredcheck_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

三个场景:

  1. 持续劣化 → 主动禅让
  2. 瞬态抖动 → 二阶导 + 连续计数抑制,不切换
  3. 投票决策 → 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 常见异常与对策

现象 可能原因 对策
禅让未触发 阈值过大 / 连续次数过高 / 健康分差不足 降低 thresholdconsecutive_required;检查健康数据是否随心跳交换
频繁切换(乒乓) 阈值过小 / 防抖不足 增大 thresholdconsecutive_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:如何在生产环境灰度上线?

  1. 先部署 5Draft 但设 consecutive_required = INT_MAX(只采集不禅让);
  2. 观察 1~2 周健康分日志,确定合理 threshold
  3. 逐步恢复 consecutive_required 默认值,观察禅让行为;
  4. 全量放开。

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 开源协议章节。

相关推荐
Shadow(⊙o⊙)42 分钟前
Linux网络——IP协议 子网 路由 IP的分片字段
服务器·网络·tcp/ip
wangqiaowq1 小时前
langfuse linux下的安装部署
linux·运维·服务器
富士康质检员张全蛋1 小时前
Kafka实战 自定义分区器 几种常见的分区器
分布式·kafka
码匠许师傅1 小时前
【设计模式精讲】21.迭代器模式(Iterator)
c++·设计模式·rpc·迭代器模式·软件工程·uml
YYYing.1 小时前
【C++进阶系列 (一)】关于线程堆栈的那些事 (上篇)
c语言·开发语言·c++·线程堆栈
Huangjin007_1 小时前
【Linux 系统篇(十七)】进程(五) :进程切换、O(1) 调度算法
linux·运维·服务器
努力努力再努力wz1 小时前
【Redis入门系列】:从 RESP 协议到 redis-plus-plus:Redis 客户端编程与 C++ 接口设计
开发语言·数据库·c++·redis·分布式·缓存·架构
weixin_464307631 小时前
linux虚拟机断网
linux·运维·服务器
ly-272531 小时前
IEEE PDF eXpress终稿检测踩坑记录:PDF图片字体未嵌入与LaTeX参考文献编译异常解决方法
服务器·人工智能·算法