其他分享
首页 > 其他分享> > 分布式一致性 Raft 与 JRaft

分布式一致性 Raft 与 JRaft

作者:互联网

分布式共识算法 (Consensus Algorithm)

如何理解分布式共识?

有哪些分布式共识算法?

什么是 Raft?

Raft 是一种更易于理解的分布式共识算法,核心协议本质上还是师承 paxos 的精髓,不同的是依靠 raft 模块化的拆分以及更加简化的设计,raft 协议相对更容易实现。

模块化的拆分主要体现在:Raft 把一致性协议划分为 Leader 选举、MemberShip 变更、日志复制、Snapshot 等几个几乎完全解耦的模块

更加简化的设计则体现在:Raft 不允许类似 paxos 中的乱序提交、简化系统中的角色状态(只有 Leader、Follower、Candidate三种角色)、限制仅 Leader 可写入、使用随机化的超时时间来设计 Leader Election 等等

特点:Strong Leader

  1. 系统中必须存在且同一时刻只能有一个 leader,只有 leader 可以接受 clients 发过来的请求
  2. Leader 负责主动与所有 followers 通信,负责将’提案’发送给所有 followers,同时收集多数派的 followers 应答
  3. Leader 还需向所有 followers 主动发送心跳维持领导地位(保持存在感)

一句话总结 Strong Leader: “你们不要 BB! 按我说的做,做完了向我汇报!” 另外,身为 leader 必须保持一直 BB(heartbeat) 的状态,否则就会有别人跳出来想要 BB

strong-leader.png | left | 350x250

复制状态机

对于一个无限增长的序列 a[1, 2, 3…],如果对于任意整数 i,a[i] 的值满足分布式一致性,这个系统就满足一致性状态机的要求 基本上所有的真实系统都会有源源不断的操作,这时候单独对某个特定的值达成一致显然是不够的。为了让真实系统保证所有的副本的一致性,通常会把操作转化为 write-ahead-log(WAL)。然后让系统中所有副本对 WAL 保持一致,这样每个副本按照顺序执行 WAL 里的操作,就能保证最终的状态是一致的

st.png | left | 450x250

  1. Client 向 leader 发送写请求
  2. Leader 把’操作’转化为 WAL 写本地 log 的同时也将 log 复制到所有 followers
  3. Leader 收到多数派应答, 将 log 对应的’操作’ 应用到状态机
  4. 回复 client 处理结果

Raft 中的基本概念

Raft-node 的 3 种角色/状态

raft-node | left | 400x250

  1. Follower:完全被动,不能发送任何请求,只接受并响应来自 leader 和 candidate 的 message,每个节点启动后的初始状态一定是 follower
  2. Leader:处理所有来自客户端的请求,以及复制 log 到所有 followers
  3. Candidate:用来竞选一个新 leader (candidate 由 follower 触发超时而来)

Message 的 3 种类型

  1. RequestVote RPC:由 candidate 发出,用于发送投票请求
  2. AppendEntries (Heartbeat) RPC:由 leader 发出,用于 leader 向 followers 复制日志条目,也会用作 Heartbeat (日志条目为空即为 Heartbeat)
  3. InstallSnapshot RPC:由 leader 发出,用于快照传输,虽然多数情况都是每个服务器独立创建快照,但是leader 有时候必须发送快照给一些落后太多的 follower,这通常发生在 leader 已经丢弃了下一条要发给该follower 的日志条目(Log Compaction 时清除掉了) 的情况下

任期逻辑时钟

  1. 时间被划分为一个个任期 (term),term id 按时间轴单调递增
  2. 每一个任期的开始都是 leader 选举,选举成功之后,leader 在任期内管理整个集群,也就是 ‘选举 + 常规操作’
  3. 每个任期最多一个 leader,可能没有 leader (spilt-vote 导致)

term.png | left | 500x200

Raft 功能分解

Leader 选举

safe-term | left | 450x80

日志复制

log-replication | left | 450x200

log-replication-2.png | left | 400x150

Commit Index 推进

AppendEntries RPC

阶段小结:现在我们能用 raft 做什么?

什么是 JRaft?

JRaft 是一个基于 RAFT 一致性算法的生产级高性能 Java 实现,支持 MULTI-RAFT-GROUP,适用于高负载低延迟的场景。 使用 JRaft 你可以专注于自己的业务领域,由 JRaft 负责处理所有与 RAFT 相关的技术难题,并且 JRaft 非常易于使用,你可以通过几个示例在很短的时间内掌握它。

JRaft 是从百度的 braft 移植而来,做了一些优化和改进,感谢百度 braft 团队开源了如此优秀的 C++ RAFT 实现

JRaft 整体功能&性能优化

feature | left | 500x450

功能支持

snapshot.png | left | 250x200

性能优化

除了功能上的完整性,JRaft 还做了很多性能方面的优化,这里有一份 KV 场景(get/put)的 benchmark 数据, 在小数据包,读写比例为 9:1,保证线性一致读的场景下,三副本最高可以达到 40w+ 的 ops。

这里挑重点介绍几个优化点:

JRaft 设计

jraft-design | left | 700x550

JRaft Group

单个节点的 JRaft-node 是没什么实际意义的,下面是三副本的 JRaft 架构图

jraft-group | left | 700x550

JRaft Multi Group

单个 Raft group 是无法解决大流量的读写瓶颈的,JRaft 自然也要支持 multi-raft-group

jraft-multi-group | left | 700x550

JRaft 实现细节解析之高效的线性一致读

什么是线性一致读? 所谓线性一致读,一个简单的例子就是在 t1 的时刻我们写入了一个值,那么在 t1 之后,我们一定能读到这个值,不可能读到 t1 之前的旧值 (想想 java 中的 volatile 关键字,说白了线性一致读就是在分布式系统中实现 java volatile 语义)

read-only-safe | left | 700x250

如上图 Client A、B、C、D 均符合线性一致读,其中 D 看起来是 stale read,其实并不是,D 请求横跨了 3 个阶段,而读可能发生在任意时刻,所以读到 1 或 2 都行

重要:接下来的讨论均基于一个大前提,就是业务状态机的实现必须是满足线性一致性的,简单说就是也要具有 java volatile 的语义

在 JRaft 中发起一次线性一致读请求的代码展示:

// KV 存储实现线性一致读
public void readFromQuorum(String key, AsyncContext asyncContext) {
    // 请求 ID 作为请求上下文传入
    byte[] reqContext = new byte[4];
    Bits.putInt(reqContext, 0, requestId.incrementAndGet());
    // 调用 readIndex 方法, 等待回调执行
    this.node.readIndex(reqContext, new ReadIndexClosure() {

        @Override
        public void run(Status status, long index, byte[] reqCtx) {
            if (status.isOk()) {
                try {
                    // ReadIndexClosure 回调成功,可以从状态机读取最新数据返回
                    // 如果你的状态实现有版本概念,可以根据传入的日志 index 编号做读取
                    asyncContext.sendResponse(new ValueCommand(fsm.getValue(key)));
                } catch (KeyNotFoundException e) {
                    asyncContext.sendResponse(GetCommandProcessor.createKeyNotFoundResponse());
                }
            } else {
                // 特定情况下,比如发生选举,该读请求将失败
                asyncContext.sendResponse(new BooleanCommand(false, status.getErrorMsg()));
            }
        }
    });
}

JRaft 应用场景?

  1. Leader 选举
  2. 分布式锁服务,比如 zookeeper,在 JRaft 中的 RheaKV 模块提供了完整的分布式锁实现
  3. 高可靠的元信息管理,可直接基于 JRaft-RheaKV 存储
  4. 分布式存储系统,如分布式消息队列、分布式文件系统、分布式块系统等等

使用案例

  1. RheaKV:基于 JRaft 实现的嵌入式、分布式、高可用、强一致的 KV 存储类库
  2. AntQ Streams QCoordinator:使用 JRaft 在 Coordinator 集群内做选举、使用 JRaft-RheaKV 做元信息存储等功能
  3. Schema Registry:高可靠 schema 管理服务,类似 kafka schema registry,存储部分基于 JRaft-RheaKV
  4. SOFA 服务注册中心元信息管理模块:IP 数据信息注册,要求写数据达到各个节点一致,并且在少数派节点挂掉时保证不影响数据正常存储

实践

一、基于 JRaft 设计一个简单的 KV Store

kv | left | 700x550

二、基于 JRaft 的 RheaKV 的设计

rheakv | left | 700x550

功能名词

PD 全局的中心总控节点,负责整个集群的调度,不需要自管理的集群可不启用 PD (一个 PD 可管理多个集群,基于 clusterId 隔离)

Store 集群中的一个物理存储节点,一个 store 包含一个或多个 region

Region 最小的 KV 数据单元,每个 region 都有一个左闭右开的区间 [startKey, endKey), 可根据请求流量/负载/数据量大小等指标自动分裂以及自动副本搬迁

特点

以上几点(尤其2,3) 基本都是依托于 JRaft 自身的功能来实现,详细介绍请参考 JRaft 文档

JRaft 详细文档

致谢

感谢 braftetcdtikv 贡献了优秀的 raft 实现,JRaft 受益良多

招聘

蚂蚁金服中间件团队持续在寻找对于基础中间件(如消息、数据中间件以及分布式计算等)以及下一代高性能面向实时分析的时序数据库等方向充满热情的小伙伴加入,有意者请联系 boyan@antfin.com

参考资料

  原文:https://www.sofastack.tech/projects/sofa-jraft/consistency-raft-jraft/

标签:log,JRaft,节点,状态机,Raft,日志,leader,分布式
来源: https://www.cnblogs.com/yulinfu/p/15410107.html