分布式嵌入式系统——多节点协同与一致性协议

文章目录

      • 每日一句正能量
      • 一、引言:当嵌入式系统走向分布式
      • [二、分布式一致性:从 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 提出)的设计目标就是可理解性。它将复杂的一致性协议分解为三个相对独立的子问题:

  1. Leader 选举(Leader Election):选出一个 Leader 节点协调所有变更
  2. 日志复制(Log Replication):Leader 将操作日志复制到所有 Follower
  3. 安全性(Safety):保证已提交的日志不会被覆盖

研究表明,Raft 的学习曲线显著优于 Paxos,目前已有超过 100 个开源实现,成为 etcd、Consul、TiKV 等系统的核心共识协议。


三、Raft 算法核心:Leader 选举与日志复制

3.1 算法核心流程

Leader 选举流程

  1. 超时变 Candidate:每个 Follower 维护一个随机选举超时(通常 150-300ms),超时后变为 Candidate
  2. 发起投票 :Candidate 自增任期(Term),向所有节点发送 RequestVote RPC
  3. 赢得选举 :获得超过半数节点投票后成为 Leader,立即发送心跳(AppendEntries)确立权威

日志复制流程

  1. Client 请求:Client 向 Leader 发送写请求
  2. Leader 追加:Leader 将请求作为新日志条目追加到本地日志
  3. 并行复制 :Leader 并行发送 AppendEntries RPC 到所有 Follower
  4. 多数确认:收到超过半数 Follower 的确认后,Leader 提交该日志
  5. 应用状态机: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 算法出发,针对嵌入式资源受限场景进行了大幅简化:

  1. 固定 3 节点:编译期确定成员,无需动态成员变更
  2. 单日志流:一个 Raft Group 管理所有状态数据
  3. 合并 RPC:心跳与日志 Append 合并为一条消息
  4. 内存优先:日志缓存在 RAM,定期批量刷 Flash
  5. 增量同步:基于版本向量的变更日志,节省 90%+ 带宽

在 HarmonyOS 软总线之上,这套简化 Raft 可以实现:

  • 智能家居多设备状态一致性(灯光、窗帘、空调联动)
  • 车载系统与手机的无缝任务迁移
  • 工业控制器分布式冗余与故障切换
  • 医疗设备跨终端数据同步

分布式一致性不是"大系统"的专利,通过合理的简化与优化,嵌入式设备同样可以实现高可靠的分布式协同。


转载自:https://blog.csdn.net/u014727709/article/details/162628785

欢迎 👍点赞✍评论⭐收藏,欢迎指正

相关推荐
至乐活着2 天前
深入浅出CAP理论:从原理到实战,用Go实现一个最终一致性的分布式键值存储
go语言·分布式系统·实战教程·cap理论·最终一致性
我心飞翔@坚持不懈1 个月前
高并发高可用电商平台交易架构实战(避坑)指南
高并发·架构设计·高可用·分布式系统·电商架构·架构避坑
better_liang1 个月前
每日Java面试场景题知识点之-数据库与缓存的一致性
java·数据库·redis·面试·分布式系统·缓存一致性·cache aside
一切皆是因缘际会1 个月前
从模型竞赛到全域智能的时代跃迁
人工智能·深度学习·ai·分布式系统
这是谁的博客?1 个月前
微服务架构设计模式深度解析:从拆分策略到容灾机制
微服务·设计模式·云原生·架构·架构设计·后端开发·分布式系统
西***63471 个月前
讯维分布式系统管控软件:理解指挥中心信号调度的底层逻辑
分布式系统·指挥调度
西***63472 个月前
讯维分布式可视化控制系统:重塑应急调度的智能响应体系
分布式系统·分布式可视化系统·分布式输入输出节点
西***63472 个月前
讯维分布式双路节点:国产音视频核心硬件,筑牢指挥中心与高端会议数字底座
分布式系统·分布式双路节点·音视频分布式
柱子jason2 个月前
IOT-Tree支持[子站-中心]数据同步功能-轻松支持你的物联网平台
物联网·工业物联网·分布式系统·iot-tree