文章目录
-
-
- 每日一句正能量
- 一、引言:当嵌入式系统走向分布式
- [二、分布式一致性:从 Paxos 到 Raft 的演进](#二、分布式一致性:从 Paxos 到 Raft 的演进)
-
- [2.1 一致性问题的本质](#2.1 一致性问题的本质)
- [2.2 Paxos 到 Raft:从"难以理解"到"工程友好"](#2.2 Paxos 到 Raft:从"难以理解"到"工程友好")
- [三、Raft 算法核心:Leader 选举与日志复制](#三、Raft 算法核心:Leader 选举与日志复制)
-
- [3.1 算法核心流程](#3.1 算法核心流程)
- [3.2 安全保证](#3.2 安全保证)
- [四、嵌入式场景下的 Raft 简化](#四、嵌入式场景下的 Raft 简化)
-
- [4.1 为什么要简化?](#4.1 为什么要简化?)
- [4.2 嵌入式简化策略](#4.2 嵌入式简化策略)
- [4.3 嵌入式简化 Raft 架构](#4.3 嵌入式简化 Raft 架构)
- [五、代码实现:嵌入式简化 Raft](#五、代码实现:嵌入式简化 Raft)
-
- [5.1 核心数据结构](#5.1 核心数据结构)
- [5.2 Leader 选举实现](#5.2 Leader 选举实现)
- [5.3 日志复制与心跳合并](#5.3 日志复制与心跳合并)
- [六、状态同步:全量 vs 增量 vs 增量快照](#六、状态同步:全量 vs 增量 vs 增量快照)
-
- [6.1 三种同步机制对比](#6.1 三种同步机制对比)
- [6.2 增量同步实现](#6.2 增量同步实现)
- 七、多节点协同状态机
-
- [7.1 完整状态转换模型](#7.1 完整状态转换模型)
- [7.2 网络分区处理](#7.2 网络分区处理)
- [八、HarmonyOS 分布式协同实践](#八、HarmonyOS 分布式协同实践)
-
- [8.1 HarmonyOS 软总线 + 简化 Raft](#8.1 HarmonyOS 软总线 + 简化 Raft)
- 九、性能优化与测试验证
-
- [9.1 关键性能指标](#9.1 关键性能指标)
- [9.2 一致性测试](#9.2 一致性测试)
- 十、总结
-

每日一句正能量
人的生命有两次,当你真正明白生命只有一次时,第二次就开始了。
多数人年轻时浑浑噩噩,把生命当成无限取用的资源。直到某次重病、失去、或突然意识到时间有限,才开始认真问:"我到底要怎样活?"那一刻,你不再为别人的期待而活,不再拖延重要的事。第一次生命是被动出生,第二次是主动觉醒。知道只有一次,反而敢活出唯一的一次。
一、引言:当嵌入式系统走向分布式
传统的嵌入式系统是"单机思维"------一个 MCU、一套固件、独立运行。但在 HarmonyOS 生态中,分布式能力打破了这种边界:手机、平板、手表、车机、音箱、IoT 设备可以组成一个超级虚拟终端,协同完成单一设备无法胜任的复杂任务。
分布式带来了能力倍增,也带来了新的挑战:如何让多个独立节点对共享状态达成一致? 当手机作为" Leader" 节点下发控制指令时,手表和音箱如何确保执行的是同一套命令?当网络分区导致部分节点失联时,系统如何避免"脑裂"(Split-Brain)?
这正是分布式一致性协议要解决的问题。本文将深入剖析 Raft 算法的核心原理,针对嵌入式资源受限场景进行大幅简化,并结合 HarmonyOS 软总线给出完整的工程实现。
二、分布式一致性:从 Paxos 到 Raft 的演进
2.1 一致性问题的本质
分布式一致性(Consensus)是指:多个节点在存在故障、网络延迟、网络分区的情况下,对某个值或状态达成一致。
经典的"两将军问题"和"拜占庭将军问题"已经证明:在异步网络中,如果没有任何限制,一致性是不可能保证的。因此,实际系统采用"多数派原则"------只要超过半数(N/2 + 1)的节点达成一致,系统即可继续运行。
2.2 Paxos 到 Raft:从"难以理解"到"工程友好"
Paxos 算法由 Leslie Lamport 于 1990 年提出,是分布式一致性的理论基石。但 Paxos 以其晦涩难懂著称,Google 工程师曾评价:"世界上只有两种人------以为自己懂 Paxos 的人,和真正懂 Paxos 的人。"
Raft 算法(2014 年由 Diego Ongaro 和 John Ousterhout 提出)的设计目标就是可理解性。它将复杂的一致性协议分解为三个相对独立的子问题:
- Leader 选举(Leader Election):选出一个 Leader 节点协调所有变更
- 日志复制(Log Replication):Leader 将操作日志复制到所有 Follower
- 安全性(Safety):保证已提交的日志不会被覆盖
研究表明,Raft 的学习曲线显著优于 Paxos,目前已有超过 100 个开源实现,成为 etcd、Consul、TiKV 等系统的核心共识协议。
三、Raft 算法核心:Leader 选举与日志复制
3.1 算法核心流程

Leader 选举流程:
- 超时变 Candidate:每个 Follower 维护一个随机选举超时(通常 150-300ms),超时后变为 Candidate
- 发起投票 :Candidate 自增任期(Term),向所有节点发送
RequestVoteRPC - 赢得选举 :获得超过半数节点投票后成为 Leader,立即发送心跳(
AppendEntries)确立权威
日志复制流程:
- Client 请求:Client 向 Leader 发送写请求
- Leader 追加:Leader 将请求作为新日志条目追加到本地日志
- 并行复制 :Leader 并行发送
AppendEntriesRPC 到所有 Follower - 多数确认:收到超过半数 Follower 的确认后,Leader 提交该日志
- 应用状态机:Leader 将已提交的日志应用到状态机,并通知 Client 和所有 Follower
3.2 安全保证
Raft 通过以下规则保证安全性:
- 选举安全:每任期最多一个 Leader 被选出
- 日志匹配:如果两个日志条目具有相同的索引和任期,则它们存储相同的命令
- Leader 完备性:如果某日志条目在某任期被提交,则该日志条目必然存在于所有后续任期的 Leader 中
- 状态机安全性:如果某节点将某日志条目应用到状态机,则其他节点不会在相同索引处应用不同的命令
四、嵌入式场景下的 Raft 简化
4.1 为什么要简化?
标准 Raft 算法设计目标是通用分布式系统(如 etcd、Consul),其假设是:
- 节点数量可变(支持动态成员变更)
- 网络带宽充足(支持频繁 RPC)
- 存储资源充裕(支持大日志和快照)
- 多 Raft Group(支持分片扩展)
这些假设在嵌入式场景中大多不成立:
- 节点数量固定(3-5 个设备组成一个分布式组)
- 网络带宽受限(BLE、Zigbee、低速 WiFi)
- 存储资源紧张(RAM 可能只有几十 KB)
- 实时性要求(控制指令必须在毫秒级达成一致)
因此,我们需要对 Raft 进行大幅简化,在保留核心一致性的前提下,大幅降低资源消耗。
4.2 嵌入式简化策略

| 简化项 | 标准 Raft | 嵌入式简化 Raft |
|---|---|---|
| 成员管理 | 动态增删节点 | 编译期固定 3 节点 |
| 日志管理 | 多日志流 + 快照 | 单日志流 + 内存缓存 |
| RPC 设计 | 心跳 + AppendEntries 分离 | 合并为一条消息 |
| 存储方式 | 磁盘持久化 | 内存优先 + 定期刷 Flash |
| 代码量 | ~2000 行 | ~500 行 |
| 内存占用 | ~10MB+ | ~50KB |
4.3 嵌入式简化 Raft 架构

五、代码实现:嵌入式简化 Raft
5.1 核心数据结构
c
/* embedded_raft.h - 嵌入式简化Raft实现 */
#ifndef EMBEDDED_RAFT_H
#define EMBEDDED_RAFT_H
#include <stdint.h>
#include <stdbool.h>
/* 配置常量:编译期确定 */
#define RAFT_NODE_COUNT 3 /* 固定3节点 */
#define RAFT_LOG_MAX_SIZE 256 /* 最大日志条目数 */
#define RAFT_TERM_MAX 65535 /* 最大任期 */
#define RAFT_ELECTION_TIMEOUT_MIN 150 /* 最小选举超时(ms) */
#define RAFT_ELECTION_TIMEOUT_MAX 300 /* 最大选举超时(ms) */
#define RAFT_HEARTBEAT_INTERVAL 50 /* 心跳间隔(ms) */
/* 节点角色 */
typedef enum {
RAFT_ROLE_FOLLOWER = 0,
RAFT_ROLE_CANDIDATE,
RAFT_ROLE_LEADER
} RaftRole_t;
/* 日志条目 */
typedef struct {
uint32_t term; /* 任期号 */
uint32_t index; /* 日志索引 */
uint8_t data[64]; /* 数据载荷 */
uint8_t data_len; /* 数据长度 */
bool committed; /* 是否已提交 */
} RaftLogEntry_t;
/* 节点状态 */
typedef struct {
uint32_t node_id; /* 本节点ID */
RaftRole_t role; /* 当前角色 */
uint32_t current_term; /* 当前任期 */
uint32_t voted_for; /* 当前任期投票给谁 */
uint32_t leader_id; /* 当前Leader ID */
/* 日志 */
RaftLogEntry_t log[RAFT_LOG_MAX_SIZE];
uint32_t log_count; /* 日志条目数 */
uint32_t commit_index; /* 已提交的最大索引 */
uint32_t last_applied; /* 已应用到状态机的最大索引 */
/* 选举相关 */
uint32_t election_timeout; /* 随机选举超时 */
uint32_t last_heartbeat; /* 上次收到心跳时间 */
uint32_t votes_received; /* 收到的票数 */
/* 状态机 */
void (*state_machine_apply)(const uint8_t *data, uint8_t len);
/* 网络回调 */
int (*send_rpc)(uint32_t target_id, uint8_t msg_type, const void *data, uint16_t len);
} RaftNode_t;
/* RPC消息类型 */
#define RAFT_MSG_REQUEST_VOTE 0x01
#define RAFT_MSG_REQUEST_VOTE_RESP 0x02
#define RAFT_MSG_APPEND_ENTRIES 0x03
#define RAFT_MSG_APPEND_ENTRIES_RESP 0x04
#define RAFT_MSG_HEARTBEAT 0x05 /* 合并的心跳+空日志 */
/* API */
int Raft_Init(RaftNode_t *node, uint32_t node_id,
void (*apply_fn)(const uint8_t*, uint8_t),
int (*send_fn)(uint32_t, uint8_t, const void*, uint16_t));
int Raft_Tick(RaftNode_t *node, uint32_t now_ms); /* 主循环tick */
int Raft_OnMessage(RaftNode_t *node, uint32_t from_id,
uint8_t msg_type, const void *data, uint16_t len);
int Raft_Propose(RaftNode_t *node, const uint8_t *data, uint8_t len);
#endif /* EMBEDDED_RAFT_H */
5.2 Leader 选举实现
c
/* raft_election.c - Leader选举实现 */
#include \"embedded_raft.h\"
#include <string.h>
/* 节点ID列表(编译期固定) */
static const uint32_t g_nodeIds[RAFT_NODE_COUNT] = {0x01, 0x02, 0x03};
/* 初始化节点 */
int Raft_Init(RaftNode_t *node, uint32_t node_id,
void (*apply_fn)(const uint8_t*, uint8_t),
int (*send_fn)(uint32_t, uint8_t, const void*, uint16_t)) {
memset(node, 0, sizeof(RaftNode_t));
node->node_id = node_id;
node->role = RAFT_ROLE_FOLLOWER;
node->current_term = 0;
node->voted_for = 0;
node->leader_id = 0;
node->election_timeout = RAFT_ELECTION_TIMEOUT_MIN +
(node_id * 50) % (RAFT_ELECTION_TIMEOUT_MAX - RAFT_ELECTION_TIMEOUT_MIN);
node->last_heartbeat = 0;
node->state_machine_apply = apply_fn;
node->send_rpc = send_fn;
Log_Info(\"Raft node %u initialized, election_timeout=%u ms\",
node_id, node->election_timeout);
return 0;
}
/* 主循环tick:驱动选举超时和心跳 */
int Raft_Tick(RaftNode_t *node, uint32_t now_ms) {
switch (node->role) {
case RAFT_ROLE_FOLLOWER:
/* 检查选举超时 */
if (now_ms - node->last_heartbeat > node->election_timeout) {
Log_Info(\"Node %u: election timeout, becoming candidate\", node->node_id);
Raft_BecomeCandidate(node, now_ms);
}
break;
case RAFT_ROLE_CANDIDATE:
/* 检查选举超时(未赢得选举) */
if (now_ms - node->last_heartbeat > node->election_timeout) {
Log_Warn(\"Node %u: election timeout, retrying\", node->node_id);
Raft_BecomeCandidate(node, now_ms); /* 重新发起选举 */
}
break;
case RAFT_ROLE_LEADER:
/* 发送心跳(合并AppendEntries) */
if (now_ms - node->last_heartbeat > RAFT_HEARTBEAT_INTERVAL) {
Raft_SendHeartbeat(node);
node->last_heartbeat = now_ms;
}
break;
}
/* 应用已提交的日志到状态机 */
while (node->last_applied < node->commit_index) {
node->last_applied++;
RaftLogEntry_t *entry = &node->log[node->last_applied - 1];
if (node->state_machine_apply) {
node->state_machine_apply(entry->data, entry->data_len);
}
Log_Debug(\"Applied log entry %u (term=%u)\", node->last_applied, entry->term);
}
return 0;
}
/* 变为Candidate,发起选举 */
static void Raft_BecomeCandidate(RaftNode_t *node, uint32_t now_ms) {
node->role = RAFT_ROLE_CANDIDATE;
node->current_term++;
node->voted_for = node->node_id; /* 投自己一票 */
node->votes_received = 1;
node->last_heartbeat = now_ms;
Log_Info(\"Node %u became Candidate for term %u\", node->node_id, node->current_term);
/* 向所有其他节点发送投票请求 */
for (uint8_t i = 0; i < RAFT_NODE_COUNT; i++) {
if (g_nodeIds[i] != node->node_id) {
Raft_RequestVote_t req = {
.term = node->current_term,
.candidate_id = node->node_id,
.last_log_index = node->log_count,
.last_log_term = (node->log_count > 0) ? node->log[node->log_count - 1].term : 0
};
node->send_rpc(g_nodeIds[i], RAFT_MSG_REQUEST_VOTE, &req, sizeof(req));
}
}
}
/* 处理投票请求 */
static int Raft_HandleRequestVote(RaftNode_t *node, uint32_t from_id,
const Raft_RequestVote_t *req) {
Raft_RequestVoteResp_t resp = {
.term = node->current_term,
.vote_granted = false
};
/* 任期检查 */
if (req->term < node->current_term) {
/* 请求者任期落后,拒绝投票 */
resp.term = node->current_term;
node->send_rpc(from_id, RAFT_MSG_REQUEST_VOTE_RESP, &resp, sizeof(resp));
return 0;
}
if (req->term > node->current_term) {
/* 发现更高任期,转为Follower */
node->current_term = req->term;
node->role = RAFT_ROLE_FOLLOWER;
node->voted_for = 0;
node->leader_id = 0;
}
/* 投票检查 */
if ((node->voted_for == 0 || node->voted_for == req->candidate_id) &&
Raft_IsLogUpToDate(node, req->last_log_index, req->last_log_term)) {
resp.vote_granted = true;
node->voted_for = req->candidate_id;
node->last_heartbeat = Platform_GetTickMs(); /* 重置选举超时 */
Log_Info(\"Node %u voted for %u in term %u\", node->node_id, req->candidate_id, req->term);
}
node->send_rpc(from_id, RAFT_MSG_REQUEST_VOTE_RESP, &resp, sizeof(resp));
return 0;
}
/* 处理投票响应 */
static int Raft_HandleRequestVoteResp(RaftNode_t *node, uint32_t from_id,
const Raft_RequestVoteResp_t *resp) {
if (node->role != RAFT_ROLE_CANDIDATE) {
return 0; /* 已经不是Candidate,忽略 */
}
if (resp->term > node->current_term) {
/* 发现更高任期,转为Follower */
node->current_term = resp->term;
node->role = RAFT_ROLE_FOLLOWER;
node->voted_for = 0;
return 0;
}
if (resp->vote_granted) {
node->votes_received++;
Log_Info(\"Node %u received vote from %u (%u/%u)\",
node->node_id, from_id, node->votes_received, RAFT_NODE_COUNT);
/* 赢得多数票(3节点中2票即可) */
if (node->votes_received > RAFT_NODE_COUNT / 2) {
Raft_BecomeLeader(node);
}
}
return 0;
}
/* 变为Leader */
static void Raft_BecomeLeader(RaftNode_t *node) {
node->role = RAFT_ROLE_LEADER;
node->leader_id = node->node_id;
node->last_heartbeat = Platform_GetTickMs();
Log_Info(\"Node %u became Leader for term %u\", node->node_id, node->current_term);
/* 立即发送心跳,确立权威 */
Raft_SendHeartbeat(node);
}
/* 检查日志是否最新 */
static bool Raft_IsLogUpToDate(RaftNode_t *node, uint32_t last_log_index, uint32_t last_log_term) {
uint32_t my_last_term = (node->log_count > 0) ? node->log[node->log_count - 1].term : 0;
if (last_log_term > my_last_term) {
return true;
}
if (last_log_term == my_last_term && last_log_index >= node->log_count) {
return true;
}
return false;
}
5.3 日志复制与心跳合并
c
/* raft_log_replication.c - 日志复制与心跳 */
#include \"embedded_raft.h\"
/* 合并的心跳+日志同步消息 */
typedef struct {
uint32_t term;
uint32_t leader_id;
uint32_t prev_log_index;
uint32_t prev_log_term;
uint32_t leader_commit; /* Leader已提交的索引 */
uint8_t entry_count; /* 本次携带的日志条目数 (0表示纯心跳) */
/* 变长日志条目跟随 */
} Raft_AppendEntries_t;
/* 发送心跳(合并AppendEntries) */
static void Raft_SendHeartbeat(RaftNode_t *node) {
if (node->role != RAFT_ROLE_LEADER) return;
for (uint8_t i = 0; i < RAFT_NODE_COUNT; i++) {
if (g_nodeIds[i] == node->node_id) continue;
Raft_AppendEntries_t msg = {
.term = node->current_term,
.leader_id = node->node_id,
.prev_log_index = node->log_count,
.prev_log_term = (node->log_count > 0) ? node->log[node->log_count - 1].term : 0,
.leader_commit = node->commit_index,
.entry_count = 0 /* 纯心跳,不携带日志 */
};
node->send_rpc(g_nodeIds[i], RAFT_MSG_HEARTBEAT, &msg, sizeof(msg));
}
}
/* 客户端发起提案(写操作) */
int Raft_Propose(RaftNode_t *node, const uint8_t *data, uint8_t len) {
if (node->role != RAFT_ROLE_LEADER) {
/* 不是Leader,转发到Leader */
if (node->leader_id != 0) {
Log_Info(\"Forwarding proposal to Leader %u\", node->leader_id);
/* 通过应用层转发... */
}
return -EPERM;
}
if (node->log_count >= RAFT_LOG_MAX_SIZE) {
Log_Error(\"Log full, cannot propose\");
return -ENOSPC;
}
/* 追加到本地日志 */
RaftLogEntry_t *entry = &node->log[node->log_count];
entry->term = node->current_term;
entry->index = node->log_count + 1;
entry->data_len = (len > sizeof(entry->data)) ? sizeof(entry->data) : len;
memcpy(entry->data, data, entry->data_len);
entry->committed = false;
node->log_count++;
Log_Info(\"Leader proposed entry %u (term=%u)\", entry->index, entry->term);
/* 立即发送给所有Follower */
Raft_ReplicateLog(node, entry);
return 0;
}
/* 复制日志到Follower */
static void Raft_ReplicateLog(RaftNode_t *node, RaftLogEntry_t *entry) {
for (uint8_t i = 0; i < RAFT_NODE_COUNT; i++) {
if (g_nodeIds[i] == node->node_id) continue;
/* 构建AppendEntries消息 */
uint8_t buffer[128];
Raft_AppendEntries_t *msg = (Raft_AppendEntries_t*)buffer;
msg->term = node->current_term;
msg->leader_id = node->node_id;
msg->prev_log_index = entry->index - 1;
msg->prev_log_term = (entry->index > 1) ? node->log[entry->index - 2].term : 0;
msg->leader_commit = node->commit_index;
msg->entry_count = 1;
/* 复制日志条目 */
memcpy(buffer + sizeof(Raft_AppendEntries_t), entry, sizeof(RaftLogEntry_t));
node->send_rpc(g_nodeIds[i], RAFT_MSG_APPEND_ENTRIES, buffer,
sizeof(Raft_AppendEntries_t) + sizeof(RaftLogEntry_t));
}
}
/* 处理AppendEntries(心跳或日志同步) */
static int Raft_HandleAppendEntries(RaftNode_t *node, uint32_t from_id,
const Raft_AppendEntries_t *msg, const uint8_t *entries) {
Raft_AppendEntriesResp_t resp = {
.term = node->current_term,
.success = false,
.match_index = 0
};
/* 任期检查 */
if (msg->term < node->current_term) {
node->send_rpc(from_id, RAFT_MSG_APPEND_ENTRIES_RESP, &resp, sizeof(resp));
return 0;
}
/* 发现更高任期或新的Leader */
if (msg->term > node->current_term ||
(msg->term == node->current_term && node->role != RAFT_ROLE_FOLLOWER)) {
node->current_term = msg->term;
node->role = RAFT_ROLE_FOLLOWER;
node->voted_for = 0;
node->leader_id = msg->leader_id;
}
/* 重置选举超时 */
node->last_heartbeat = Platform_GetTickMs();
/* 日志一致性检查 */
if (msg->prev_log_index > 0) {
if (msg->prev_log_index > node->log_count ||
node->log[msg->prev_log_index - 1].term != msg->prev_log_term) {
/* 日志不一致,拒绝 */
Log_Warn(\"Node %u: log mismatch at index %u\", node->node_id, msg->prev_log_index);\
resp.match_index = node->log_count;
node->send_rpc(from_id, RAFT_MSG_APPEND_ENTRIES_RESP, &resp, sizeof(resp));
return 0;
}
}
/* 追加新日志条目 */
if (msg->entry_count > 0 && entries != NULL) {
RaftLogEntry_t *new_entry = (RaftLogEntry_t*)entries;
/* 检查是否已存在 */
if (new_entry->index <= node->log_count) {
/* 覆盖冲突条目 */
if (node->log[new_entry->index - 1].term != new_entry->term) {
node->log_count = new_entry->index - 1; /* 截断冲突部分 */
}
}
/* 追加 */
if (node->log_count < RAFT_LOG_MAX_SIZE) {
memcpy(&node->log[node->log_count], new_entry, sizeof(RaftLogEntry_t));
node->log_count++;
Log_Info(\"Node %u appended entry %u (term=%u)\",
node->node_id, new_entry->index, new_entry->term);
}
}
/* 更新commit_index */
if (msg->leader_commit > node->commit_index) {
node->commit_index = (msg->leader_commit < node->log_count) ?
msg->leader_commit : node->log_count;
}
resp.success = true;
resp.match_index = node->log_count;
node->send_rpc(from_id, RAFT_MSG_APPEND_ENTRIES_RESP, &resp, sizeof(resp));
return 0;
}
/* 处理AppendEntries响应 */
static int Raft_HandleAppendEntriesResp(RaftNode_t *node, uint32_t from_id,
const Raft_AppendEntriesResp_t *resp) {
if (node->role != RAFT_ROLE_LEADER) return 0;
if (resp->term > node->current_term) {
/* 发现更高任期,退位 */
node->current_term = resp->term;
node->role = RAFT_ROLE_FOLLOWER;
node->voted_for = 0;
node->leader_id = 0;
return 0;
}
if (resp->success) {
/* 更新该节点的match_index */
/* 简化:假设所有节点同步进度一致 */
/* 检查是否可以提交 */
/* 简化:Leader收到所有Follower确认后提交 */
static uint8_t ack_count = 0;
ack_count++;
if (ack_count >= RAFT_NODE_COUNT - 1) { /* 收到所有Follower确认 */
if (node->commit_index < node->log_count) {
node->commit_index = node->log_count;
Log_Info(\"Leader committed up to index %u\", node->commit_index);
}
ack_count = 0;
}
} else {
/* 日志不一致,需要回退重试 */
Log_Warn(\"Node %u rejected append, need retry\", from_id);
/* 简化实现:回退并重发 */
}
return 0;
}
六、状态同步:全量 vs 增量 vs 增量快照
6.1 三种同步机制对比

在嵌入式分布式系统中,状态同步有三种典型策略:
全量同步(Full Sync):
- 适用场景:新节点加入、长期断线恢复、数据损坏修复
- 缺点:带宽消耗大、同步时间长、可能阻塞正常服务
- 优化:压缩传输、分片传输
增量同步(Delta Sync):
- 适用场景:正常运行的节点间周期性同步
- 优点:带宽节省 90%+、实时性高
- 实现:基于变更日志(Change Log)或操作日志(Op Log)
增量快照(Delta Snapshot):
- 适用场景:中等断线时间(几分钟到几小时)
- 原理:定期生成状态快照,只传输与上次快照的差异块
- 优化:基于校验和(CRC/Hash)比对,只传输变化的块
6.2 增量同步实现
c
/* delta_sync.h - 增量同步框架 */
#ifndef DELTA_SYNC_H\n#define DELTA_SYNC_H
#include <stdint.h>
#include <stdbool.h>
#define SYNC_MAX_DELTA_SIZE 256 /* 最大增量大小 */
#define SYNC_VERSION_BITS 32 /* 版本号位数 */
/* 状态版本向量 */
typedef struct {
uint32_t node_id;
uint32_t version; /* 单调递增版本号 */
uint32_t timestamp; /* 时间戳 */
} VersionVector_t;
/* 变更记录 */
typedef struct {
uint32_t seq; /* 变更序列号 */
uint8_t op; /* 操作类型: 0=Add, 1=Update, 2=Delete */
uint8_t key[32]; /* 键 */
uint8_t value[64]; /* 值 */
uint8_t value_len;
VersionVector_t version;
} ChangeRecord_t;
/* 增量同步请求 */
typedef struct {
VersionVector_t since; /* 从哪个版本开始同步 */
uint32_t max_records; /* 最大请求记录数 */
} DeltaSyncRequest_t;
/* 增量同步响应 */
typedef struct {
uint32_t record_count;
VersionVector_t current_version;
bool has_more; /* 是否还有更多 */
ChangeRecord_t records[0]; /* 变长记录 */
} DeltaSyncResponse_t;
/* API */
int DeltaSync_Init(void);
int DeltaSync_RecordChange(const ChangeRecord_t *record);
int DeltaSync_RequestSync(uint32_t target_node, const VersionVector_t *since);
int DeltaSync_ApplyDeltas(const DeltaSyncResponse_t *response);
#endif
c
/* delta_sync.c - 增量同步实现 */
#include \"delta_sync.h\"
#define MAX_CHANGE_LOG 128 /* 变更日志最大条目数 */
static ChangeRecord_t g_changeLog[MAX_CHANGE_LOG];
static uint32_t g_changeLogHead = 0; /* 写入位置 */
static uint32_t g_changeLogTail = 0; /* 读取位置 */
static VersionVector_t g_localVersion = {0, 0, 0};
/* 记录变更 */
int DeltaSync_RecordChange(const ChangeRecord_t *record) {
if (!record) return -EINVAL;
/* 更新本地版本 */
g_localVersion.version++;
g_localVersion.timestamp = Platform_GetTickMs();
/* 写入变更日志 */
uint32_t idx = g_changeLogHead % MAX_CHANGE_LOG;
memcpy(&g_changeLog[idx], record, sizeof(ChangeRecord_t));
g_changeLog[idx].version = g_localVersion;
g_changeLog[idx].seq = g_localVersion.version;
g_changeLogHead++;
/* 环形缓冲区溢出处理 */
if (g_changeLogHead - g_changeLogTail > MAX_CHANGE_LOG) {
g_changeLogTail = g_changeLogHead - MAX_CHANGE_LOG;
}
return 0;
}
/* 生成增量同步响应 */
int DeltaSync_GenerateResponse(const DeltaSyncRequest_t *req,
DeltaSyncResponse_t *resp, uint16_t *resp_len) {
if (!req || !resp || !resp_len) return -EINVAL;
uint32_t since_version = req->since.version;
uint32_t record_count = 0;
/* 遍历变更日志,收集从since_version之后的变更 */
for (uint32_t i = g_changeLogTail; i < g_changeLogHead && record_count < req->max_records; i++) {
uint32_t idx = i % MAX_CHANGE_LOG;
if (g_changeLog[idx].seq > since_version) {
memcpy(&resp->records[record_count], &g_changeLog[idx], sizeof(ChangeRecord_t));
record_count++;
}
}
resp->record_count = record_count;
resp->current_version = g_localVersion;
resp->has_more = (g_changeLogHead > g_changeLogTail + record_count);
*resp_len = sizeof(DeltaSyncResponse_t) + record_count * sizeof(ChangeRecord_t);
return 0;
}
/* 应用增量 */
int DeltaSync_ApplyDeltas(const DeltaSyncResponse_t *response) {
if (!response) return -EINVAL;
for (uint32_t i = 0; i < response->record_count; i++) {
ChangeRecord_t *record = &response->records[i];
switch (record->op) {
case 0: /* Add */
StateDB_Insert(record->key, record->value, record->value_len);
break;
case 1: /* Update */
StateDB_Update(record->key, record->value, record->value_len);
break;
case 2: /* Delete */
StateDB_Delete(record->key);
break;
}
Log_Debug(\"Applied delta: op=%u, key=%s, seq=%u\", record->op, record->key, record->seq);
}
/* 更新本地版本向量 */
if (response->current_version.version > g_localVersion.version) {
g_localVersion = response->current_version;
}
return 0;
}
七、多节点协同状态机
7.1 完整状态转换模型

分布式节点在运行过程中会经历以下状态转换:
- INIT → FOLLOWER:节点启动,初始化后成为 Follower
- FOLLOWER → CANDIDATE:选举超时,变为 Candidate 发起投票
- CANDIDATE → LEADER:赢得多数票,成为 Leader
- CANDIDATE → FOLLOWER:发现新的 Leader,退位为 Follower
- LEADER → FOLLOWER:发现更高任期,自动退位
- FOLLOWER → PARTITIONED:网络分区,与 Leader 失联
- PARTITIONED → RECOVERING:网络恢复,开始日志对齐
- RECOVERING → FOLLOWER:日志对齐完成,恢复正常 Follower
- LEADER → SYNCING:新节点加入,Leader 进入同步模式
- SYNCING → CONSISTENT:新节点日志追上,达到一致性
7.2 网络分区处理
网络分区是分布式系统中最棘手的问题。在嵌入式 3 节点场景中:
多数派分区(2 个节点):
- 继续正常运行,可以提交新日志
- 少数派节点(1 个)暂停服务,避免脑裂
少数派分区(1 个节点):
- 无法获得多数票,不能提交新日志
- 进入只读模式或暂停服务
- 等待网络恢复后自动对齐日志
c
/* 网络分区检测与处理 */
static void Raft_CheckPartition(RaftNode_t *node) {
static uint32_t partition_start = 0;
if (node->role == RAFT_ROLE_LEADER) {
/* Leader检查是否能联系到多数派 */
uint8_t reachable = 1; /* 自己 */
for (uint8_t i = 0; i < RAFT_NODE_COUNT; i++) {
if (g_nodeIds[i] != node->node_id && NodeIsReachable(g_nodeIds[i])) {
reachable++;
}
}
if (reachable <= RAFT_NODE_COUNT / 2) {
/* 无法联系多数派,可能处于少数派分区 */
Log_Warn(\"Leader %u in minority partition, stepping down\", node->node_id);
node->role = RAFT_ROLE_FOLLOWER;
node->leader_id = 0;
node->last_heartbeat = Platform_GetTickMs();
}
}
else if (node->role == RAFT_ROLE_FOLLOWER && node->leader_id != 0) {
/* Follower检查Leader是否可达 */
if (!NodeIsReachable(node->leader_id)) {
if (partition_start == 0) {
partition_start = Platform_GetTickMs();
}
/* 分区持续时间超过阈值,尝试发起选举 */
if (Platform_GetTickMs() - partition_start > RAFT_ELECTION_TIMEOUT_MAX * 2) {
Log_Warn(\"Follower %u detected long partition, initiating election\", node->node_id);
Raft_BecomeCandidate(node, Platform_GetTickMs());
partition_start = 0;
}
} else {
partition_start = 0; /* 分区恢复 */
}
}
}
八、HarmonyOS 分布式协同实践
8.1 HarmonyOS 软总线 + 简化 Raft

HarmonyOS 的分布式能力建立在**软总线(SoftBus)**之上,软总线提供了设备发现、数据传输、状态同步的基础能力。在此基础上,我们可以构建轻量级的 Raft 一致性层,实现跨设备的状态一致性。
c
/* harmonyos_distributed_raft.c - HarmonyOS分布式Raft集成 */
#include \"softbus_bus_center.h\"
#include \"distributed_scheduler.h\"
#include \"embedded_raft.h\"
/* HarmonyOS设备节点信息 */
typedef struct {
char deviceId[64];
uint32_t nodeId;
NodeState_t state;
bool isLocal;
} HarmonyDevice_t;
/* 分布式Raft上下文 */
typedef struct {
RaftNode_t raft;
HarmonyDevice_t devices[RAFT_NODE_COUNT];
uint8_t deviceCount;
char localDeviceId[64];
} DistributedRaftContext_t;
static DistributedRaftContext_t g_distributedCtx;
/* 初始化分布式Raft */
int DistributedRaft_Init(const char *localDeviceId) {
memset(&g_distributedCtx, 0, sizeof(g_distributedCtx));
strncpy(g_distributedCtx.localDeviceId, localDeviceId, sizeof(g_distributedCtx.localDeviceId) - 1);
/* 注册软总线设备发现回调 */
SoftBus_RegisterDeviceListener(OnDeviceFound, OnDeviceLost);
/* 初始化Raft节点 */
uint32_t nodeId = HashDeviceId(localDeviceId);
Raft_Init(&g_distributedCtx.raft, nodeId,
HarmonyOS_StateMachineApply,
HarmonyOS_SendRaftRPC);
/* 启动设备发现 */
SoftBus_StartDiscovery(\"raft_group\", DISCOVER_MODE_ACTIVE);
Log_Info(\"DistributedRaft initialized for device %s (nodeId=%u)\",
localDeviceId, nodeId);
return 0;
}
/* 设备发现回调 */
static void OnDeviceFound(const DeviceInfo *info) {
/* 检查是否属于同一Raft组 */
if (!IsSameRaftGroup(info->deviceId, g_distributedCtx.localDeviceId)) {
return;
}
uint32_t nodeId = HashDeviceId(info->deviceId);
/* 添加到设备列表 */
for (uint8_t i = 0; i < RAFT_NODE_COUNT; i++) {
if (g_distributedCtx.devices[i].nodeId == 0) {
strncpy(g_distributedCtx.devices[i].deviceId, info->deviceId, 64);
g_distributedCtx.devices[i].nodeId = nodeId;
g_distributedCtx.devices[i].state = NODE_ONLINE;
g_distributedCtx.devices[i].isLocal = false;
g_distributedCtx.deviceCount++;
Log_Info(\"Raft peer discovered: %s (nodeId=%u)\", info->deviceId, nodeId);
break;
}
}
/* 建立软总线连接 */
SoftBus_ConnectDevice(info->deviceId, \"raft_channel\");
}
/* 通过软总线发送Raft RPC */
static int HarmonyOS_SendRaftRPC(uint32_t target_id, uint8_t msg_type,
const void *data, uint16_t len) {
/* 查找目标设备ID */
const char *targetDevice = NULL;
for (uint8_t i = 0; i < RAFT_NODE_COUNT; i++) {
if (g_distributedCtx.devices[i].nodeId == target_id) {
targetDevice = g_distributedCtx.devices[i].deviceId;
break;
}
}
if (!targetDevice) {
Log_Error(\"Target node %u not found\", target_id);
return -ENODEV;
}
/* 构建软总线消息 */
SoftBusMessage_t msg = {
.type = SOFTBUS_MSG_TYPE_DATA,
.channel = \"raft_channel\",
.payload = data,
.payloadLen = len
};
/* 添加Raft消息类型到头部 */
uint8_t header[1] = {msg_type};
SoftBus_SendMessage(targetDevice, &msg, header, sizeof(header));
return 0;
}
/* 状态机应用:HarmonyOS分布式状态更新 */
static void HarmonyOS_StateMachineApply(const uint8_t *data, uint8_t len) {
/* 解析状态更新 */
DistributedStateUpdate_t update;
if (ParseStateUpdate(data, len, &update) != 0) {
Log_Error(\"Failed to parse state update\");
return;
}
/* 应用到本地状态 */
switch (update.type) {
case STATE_UPDATE_DEVICE_CAPABILITY:
UpdateDeviceCapability(update.deviceId, &update.capability);
break;
case STATE_UPDATE_TASK_ASSIGNMENT:
/* 分布式任务分配更新 */
DistributedScheduler_UpdateTask(&update.task);
break;
case STATE_UPDATE_CONFIG:
/* 分布式配置更新 */
DistributedConfig_Apply(&update.config);
break;
case STATE_UPDATE_USER_PREFERENCE:
/* 用户偏好同步 */
UserPreference_Merge(&update.preference);
break;
}
/* 通知上层应用 */
NotifyStateChanged(update.type, &update);
}
/* 分布式写操作入口 */
int DistributedRaft_Write(const uint8_t *data, uint8_t len) {
/* 通过Raft协议提交 */
return Raft_Propose(&g_distributedCtx.raft, data, len);
}
/* 主循环 */
void DistributedRaft_Loop(void) {
uint32_t now = Platform_GetTickMs();
/* 驱动Raft状态机 */
Raft_Tick(&g_distributedCtx.raft, now);
/* 处理软总线消息 */
SoftBusMessage_t msg;
while (SoftBus_PollMessage(&msg, 0) == 0) {
if (msg.channel && strcmp(msg.channel, \"raft_channel\") == 0) {
/* 解析Raft消息 */
uint8_t msg_type = msg.header[0];
uint32_t from_id = HashDeviceId(msg.fromDevice);
Raft_OnMessage(&g_distributedCtx.raft, from_id, msg_type,
msg.payload, msg.payloadLen);
}
}
/* 检查设备健康状态 */
CheckDeviceHealth();
}
九、性能优化与测试验证
9.1 关键性能指标
| 指标 | 目标值 | 优化手段 |
|---|---|---|
| Leader 选举时间 | < 500ms | 固定超时范围、快速检测 |
| 日志复制延迟 | < 100ms | 合并心跳、批量传输 |
| 状态同步带宽 | < 10KB/s | 增量同步、压缩传输 |
| 内存占用 | < 50KB | 预分配、环形缓冲区 |
| 故障切换时间 | < 1s | 温备状态、快速接管 |
9.2 一致性测试
c
/* consistency_test.c - 一致性测试 */
#include <assert.h>
/* 测试场景:并发写操作的一致性 */
void Test_ConcurrentWrites(void) {
/* 初始化3节点 */
RaftNode_t nodes[3];
for (uint8_t i = 0; i < 3; i++) {
Raft_Init(&nodes[i], i + 1, TestStateMachine, TestSendRPC);
}
/* 模拟选举:让Node-1成为Leader */
nodes[0].role = RAFT_ROLE_LEADER;
nodes[0].leader_id = 1;
nodes[1].leader_id = 1;
nodes[2].leader_id = 1;
/* 发起多个写操作 */
for (uint8_t i = 0; i < 10; i++) {
uint8_t data[32];
snprintf((char*)data, sizeof(data), \"op_%u\", i);
Raft_Propose(&nodes[0], data, strlen((char*)data) + 1);
}
/* 模拟日志复制到所有Follower */
for (uint8_t i = 0; i < 10; i++) {
/* 复制到Node-2 */
memcpy(&nodes[1].log[i], &nodes[0].log[i], sizeof(RaftLogEntry_t));
/* 复制到Node-3 */
memcpy(&nodes[2].log[i], &nodes[0].log[i], sizeof(RaftLogEntry_t));
}
nodes[0].commit_index = 10;
nodes[1].commit_index = 10;
nodes[2].commit_index = 10;
/* 验证所有节点日志一致 */
for (uint8_t i = 0; i < 10; i++) {
assert(nodes[0].log[i].term == nodes[1].log[i].term);
assert(nodes[0].log[i].term == nodes[2].log[i].term);
assert(strcmp((char*)nodes[0].log[i].data, (char*)nodes[1].log[i].data) == 0);
assert(strcmp((char*)nodes[0].log[i].data, (char*)nodes[2].log[i].data) == 0);
}
Log_Info(\"Concurrent write consistency test PASSED\");
}
/* 测试场景:网络分区后的恢复 */
void Test_NetworkPartitionRecovery(void) {
RaftNode_t nodes[3];
/* 初始化... */
/* 模拟网络分区:Node-1和Node-2在一个分区,Node-3在另一个分区 */
/* Node-1(Leader)继续提交日志 */
Raft_Propose(&nodes[0], (uint8_t*)\"partition_test\", 15);
/* Node-3无法收到日志,尝试发起选举但失败 */
/* ... */
/* 网络恢复 */
/* Node-3应该自动同步缺失的日志 */
/* ... */
/* 验证最终一致性 */
assert(nodes[0].commit_index == nodes[2].commit_index);
Log_Info(\"Network partition recovery test PASSED\");
}
十、总结
分布式一致性是 HarmonyOS 生态从"单设备智能"走向"多设备协同"的关键技术支撑。本文从 Raft 算法出发,针对嵌入式资源受限场景进行了大幅简化:
- 固定 3 节点:编译期确定成员,无需动态成员变更
- 单日志流:一个 Raft Group 管理所有状态数据
- 合并 RPC:心跳与日志 Append 合并为一条消息
- 内存优先:日志缓存在 RAM,定期批量刷 Flash
- 增量同步:基于版本向量的变更日志,节省 90%+ 带宽
在 HarmonyOS 软总线之上,这套简化 Raft 可以实现:
- 智能家居多设备状态一致性(灯光、窗帘、空调联动)
- 车载系统与手机的无缝任务迁移
- 工业控制器分布式冗余与故障切换
- 医疗设备跨终端数据同步
分布式一致性不是"大系统"的专利,通过合理的简化与优化,嵌入式设备同样可以实现高可靠的分布式协同。
转载自:https://blog.csdn.net/u014727709/article/details/162628785
欢迎 👍点赞✍评论⭐收藏,欢迎指正