尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Raft如何做到线性一致性
一、为什么需要请求去重考虑一次Append(name, A)1. 客户端发送 Append 请求 2. Leader 将请求提交到 Raft 日志 3. KV 状态机执行成功name 变成 A 4. 服务端返回响应 5. 但响应在网络中丢失 6. 客户端超时重新发送同一个请求如果服务端无法识别重试Append会再次执行第一次执行name A 第二次执行name AA客户端实际上只请求了一次状态机却执行了两次这会破坏客户端期望的语义。因此必须给每个逻辑请求一个唯一身份。二、ClientId和CommandId分别是什么图中每个客户端维护两个重要信息。ClientId表示客户端身份例如客户端AClientId C1001 客户端BClientId C2001项目中Clerk创建时会生成客户端 ID并将请求序号初始化为 0Clerk::Clerk() : m_clientId(Uuid()), m_requestId(0), m_recentLeaderId(0) {}CommandId在项目中叫RequestId表示该客户端发出的第几个逻辑请求(C1001, 1) - Put(x, A) (C1001, 2) - Append(x, B) (C1001, 3) - Get(x)每次产生一个新的逻辑请求时客户端将m_requestId加一m_requestId; auto requestId m_requestId;三、为什么必须是二元组唯一请求标识是(ClientId, RequestId)不能只使用ClientId因为一个客户端会发送很多请求。不能只使用RequestId因为不同客户端都可能存在RequestId 1(C1001, 1) (C2001, 1)这显然是两个不同请求。因此ClientId RequestId共同构成一个类似“幂等键”的东西。项目中的 Raft 命令Op包含这两个字段class Op { public: std::string Operation; std::string Key; std::string Value; std::string ClientId; int RequestId; };四、重试时 RequestId 不能增加这是图中非常关键的一点。新的逻辑请求才增加RequestId第一次业务操作RequestId 10 第二次业务操作RequestId 11同一个请求因为超时而重试时必须继续使用原来的 ID发送请求(C1001, 11) 超时重试(C1001, 11) 再次重试(C1001, 11)不能变成 12、13否则服务端会认为它们是三个不同请求。这个项目做得是对的requestId在进入重试循环之前保存循环中始终复用它m_requestId; auto requestId m_requestId; while (true) { args.set_requestid(requestId); // 向不同服务器重试 }五、服务端如何判断重复请求每个 KV Server 保存std::unordered_mapstd::string, int m_lastRequestId;含义是ClientId - 该客户端已经执行过的最大 RequestId例如C1001 - 15 C2001 - 8表示C1001的 1 到 15 号请求已经处理过。判断重复请求的逻辑是return RequestId m_lastRequestId[ClientId];假设m_lastRequestId[C1001] 15那么请求 16新请求可以执行 请求 15重复请求不能再次执行 请求 14旧请求也不能再次执行这就是图中“只执行递增的 CommandId”的具体含义。六、完整的丢包重试过程客户端 C7 发送 (C7, 12, Append(x, A)) Leader 写入 Raft 日志 ↓ 多数节点复制成功 ↓ 日志被提交 ↓ KV 状态机执行 Append ↓ lastRequestId[C7] 12 ↓ 响应丢失 ↓ 客户端重试同一个 (C7, 12) ↓ 服务端发现 12 lastRequestId[C7] ↓ 不再执行 Append直接返回成功项目在 Raft 提交命令后调用ifRequestDuplicate()只有非重复请求才操作 KV 数据if (!ifRequestDuplicate(op.ClientId, op.RequestId)) { if (op.Operation Put) { ExecutePutOpOnKVDB(op); } if (op.Operation Append) { ExecuteAppendOpOnKVDB(op); } }执行 KV 操作时还会更新m_lastRequestId[op.ClientId] op.RequestId;KV 状态和去重信息在同一把互斥锁保护下更新这一点很重要。七、它与线性一致性的关系线性一致性要求每个操作看起来都在某一个瞬间原子完成而且不能违反真实时间顺序。Raft主要解决所有节点按照相同的日志顺序执行命令请求去重解决客户端的一次逻辑操作即使多次重试也只产生一次状态变化二者缺一不可。例如没有去重时Raft可能完全正确地提交两条相同的Append日志。Raft不知道它们是同一次请求的重试因为对 Raft 来说它们只是两条合法命令。所以更准确地说Raft提供一致的提交顺序ClientId RequestId提供重试情况下的至多一次执行语义二者共同支撑线性一致的客户端体验。网络层无法真正保证消息“只发送一次”这里只能保证重复消息不会导致状态机重复生效。
RELATED

相关推荐

线性一致性

线性一致性

线性一致性(Linearizability)**是分布式系统中的一种强一致性模型。一句话定义:所有操作看起来都像是在某个唯一时间点瞬间完成,并且这个时间点必须位于操作发起和响应之间,同时不能违背现实时间顺序。核心要求假设有多…

📅 2026/9/18 12:50:42
赏金不买漏洞——四个关于黑客价值的观点

赏金不买漏洞——四个关于黑客价值的观点

一句话定位赏金不是奖励,是赎金——厂商买的不是漏洞,是漏洞持有人本来可以造成的灾害。先讲清楚一个前提:黑客的能力,永远以破坏能力为尺度。无论破坏是否已经发生。发现一个能瘫痪全国电网的漏洞,和实际上有没有去瘫…

📅 2026/8/21 4:17:10
深度梳理国际工业防护面罩品牌的综合实力与选型价值

深度梳理国际工业防护面罩品牌的综合实力与选型价值

企业采购及车间管理团队,在工业防护面罩选型中普遍优先甄选优质国际品牌。而判断国际品牌面罩是否合规可靠、适配生产场景,核心评判标准并非品牌知名度,而是产品能否精准匹配企业工况条件与防护需求。一、工业防护面罩核心选型要素工业呼吸防…

📅 2026/8/21 4:17:10
MORE NEWS

更多资讯

📰

VMware虚拟机安装Windows Server 2012实战解析

虚拟机这东西,早几年是运维和开发人员的专属工具,现在做测试、跑老软件、搭实验环境的人几乎人手一个。VMware Workstation 在 Windows 平台上的地位一直很稳,尤其是需要装 Windows Server 这种重型系统做实验时,它比 Hyper-V 直观…

📰

Spring容器生命周期详解与优化实践

1. Spring容器生命周期概述在Java企业级开发中,Spring框架的核心机制就是其容器管理能力。作为开发者,我们每天都会与ApplicationContext打交道,但很少有人深入思考过容器启动和关闭过程中的那些"黑魔法"。实际上,Sprin…

📰

Unity3D火灾仿真系统:物理引擎驱动的消防预案推演与疏散验证

简介:本资源是一份面向高校安全工程、数字媒体技术及教育技术专业师生的虚拟仿真教学项目设计文档,聚焦火灾应急逃生知识的沉浸式学习场景构建。文档系统阐述了基于Unity3D引擎开发火灾仿真游戏的设计思路与实现路径,涵盖人物建模、三维场景搭…

📰

Reanimated 3 与 Gesture Handler 实战:从原理到优化,打造丝滑的 React Native 动画

Reanimated 3 出来也有一段时间了,我陆陆续续在几个项目里把它和 Gesture Handler 搭配着用,说实话,用过之后很难再退回老方案。以前写 React Native 动画,最痛苦的事情就是怎么调都差那么点意思,掉帧、卡顿、手势跟手…

📰

Unity3D游戏AI实战:状态机与行为树选型、感知与行动调优

简介:一份围绕Unity3D游戏人工智能的PDF论文资料,面向游戏AI研发人员与学习者,系统研究了行为树与机器学习(强化学习)两类主流智能体设计方法的实际应用。内容以射击游戏NPC为场景,涵盖基于视觉、听觉感知的…

📰

脑控仿生无人机:从EEG实时解码到飞行控制延迟优化

简介:一份围绕脑控仿生无人机系统设计的完整技术方案文档,面向脑机接口、机器人控制与无人机飞控方向的研究人员和工程师,重点解决EEG信号实时解码、飞行姿态控制响应延迟优化等关键问题。文档共950页,划分为60个章节,…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬