从一个例子开始体验 SOFAJRaft | SOFAChannel#8 直播整理

2024-01-17 05:08

本文主要是介绍从一个例子开始体验 SOFAJRaft | SOFAChannel#8 直播整理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

<SOFA:Channel/>,有趣实用的分布式架构频道,每月一次为大家带来技术分享。

本文根据 SOFAChannel#8 直播分享整理,主题:从一个例子开始体验 SOFAJRaft。

回顾视频以及 PPT 查看地址见文末。欢迎加入直播互动钉钉群:23390449,不错过每场直播。

640?wx_fmt=jpeg

大家好,我是力鲲,来自蚂蚁金服, 现在是 SOFAJRaft 的开源负责人。今天分享主题是《从一个例子开始体验 SOFAJRaft》,其实从这个题目大家也能看出来,今天是要从一个用户而非 owner 的视角来了解 SOFAJRaft。这么设计题目的原因是 SOFAJRaft 作为一种共识算法的实现,涉及到了一些概念和术语,而这些内容更适合通过一系列文章进行阐述,而在直播中我们希望能够分享对用户更有用、更容易理解的信息——SOFAJRaft 是什么,以及我们怎么去用它。

首先介绍一下 SOFAJRaft 的背景知识,接下来说说这个例子源于什么需求,第三部分是架构的选型,第四部分来看看我们如何使用 SOFAJRaft,最后运行代码,看看 SOFAJRaft 是如何支撑业务运行的。欢迎加入社区成为 Contributor,SOFAJRaft:https://github.com/sofastack/sofa-jraft

| Raft 共识算法

Raft 是一种共识算法,其特点是让多个参与者针对某一件事达成完全一致:一件事,一个结论。同时对已达成一致的结论,是不可推翻的。可以举一个银行账户的例子来解释共识算法:假如由一批服务器组成一个集群来维护银行账户系统,如果有一个 Client 向集群发出“存 100 元”的指令,那么当集群返回成功应答之后,Client 再向集群发起查询时,一定能够查到被存储成功的这 100 元钱,就算有机器出现不可用情况,这 100 元的账也不可篡改。这就是共识算法要达到的效果。

Raft 算法和其他的共识算法相比,又有了如下几个不同的特性:

  • Strong leader:Raft 集群中最多只能有一个 Leader,日志只能从 Leader 复制到 Follower 上;

  • Leader election:Raft 算法采用随机选举超时时间触发选举来避免选票被瓜分的情况,保证选举的顺利完成;

  • Membership changes:通过两阶段的方式应对集群内成员的加入或者退出情况,在此期间并不影响集群对外的服务;

共识算法有一个很典型的应用场景就是复制状态机。Client 向复制状态机发送一系列能够在状态机上执行的命令,共识算法负责将这些命令以 Log 的形式复制给其他的状态机,这样不同的状态机只要按照完全一样的顺序来执行这些命令,就能得到一样的输出结果。所以这就需要利用共识算法保证被复制日志的内容和顺序一致。

640?wx_fmt=png图1 - 复制状态机

| SOFAJRaft

SOFAJRaft 是基于 Raft 算法的生产级高性能 Java 实现,支持 MULTI-RAFT-GROUP。应用场景有 Leader 选举、分布式锁服务、高可靠的元信息管理、分布式存储系统。

640?wx_fmt=png图2 - SOFAJRaft 结构

这张图就是 SOFAJRaft 的设计图,Node 代表了一个 SOFAJRaft Server 节点,这些方框代表他内部的各个模块,我们依然用之前的银行账户系统举例来说明 SOFAJRaft 的各模块是如何工作的。当 Client 向 SOFAJRaft 发来一个“存 100 元”的命令之后,Node 的 Log 存储模块首先将这个命令以 Log 的形式存储到本地,同时 Replicator 会把这个 Log 复制给其他的 Node,Replicator 是有多个的,集群中有多少个 Follower 就会有多少个 Replicator,这样就能实现并发的日志复制。当 Node 收到集群中半数以上的 Follower 返回的“复制成功” 的响应之后,就可以把这条 Log 以及之前的 Log 有序的送到状态机里去执行了。状态机是由用户来实现的,比如我们现在举的例子是银行账户系统,所以状态机执行的就是账户金额的借贷操作。如果 SOFAJRaft 在别的场景中使用,状态机就会有其他的执行方式。Snapshot 是快照,所谓快照就是对数据当前值的一个记录,Leader 生成快照有这么几个作用:

  • 当有新的 Node 加入集群的时候,不用只靠日志复制、回放去和 Leader 保持数据一致,而是通过安装 Leader 的快照来跳过早期大量日志的回放;

  • Leader 用快照替代 Log 复制可以减少网络上的数据量;

  • 用快照替代早期的 Log 可以节省存储空间;

640?wx_fmt=png图3 - 需要用户实现:StateMachine、Client

SOFAJRaft 需要用户去实现两部分:StateMachine 和 Client。

因为 SOFAJRaft 只是一个工具,他的目的是帮助我们在集群内达成共识,而具体要对什么业务逻辑达成共识是需要用户自己去定义的,我们将用户需要去实现的部分定义为 StateMachine 接口。比如账务系统和分布式存储这两种业务就需要用户去实现不同的 StateMachine 逻辑。而 Client 也很好理解,根据业务的不同,用户需要去定义不同的消息类型和客户端的处理逻辑。

640?wx_fmt=png图4 - 需要用户实现一些接口

前面介绍了这么多,我们引出今天的主题:如何用 SOFAJRaft 实现一个分布式计数器?

| 需求

我们的需求其实很简单,用一句话来说就是:提供一个 Counter,Client 每次计数时可以指定步幅,也可以随时发起查询。

我们对这个需求稍作分析后,将它翻译成具体的功能点,主要有三部分:

  • 实现 Counter server,具备计数功能,具体运算公式为:Cn = Cn-1 + delta;

  • 提供写服务,写入 delta 触发计数器运算;

  • 提供读服务,读取当前 Cn 值;

除此之外,我们还有一个可用性的可选需求,需要有备份机器,读写服务不能不可用。

| 系统架构

根据刚才分析出来的功能需求,我们设计出 1.0 的架构,这个架构很简单,一个节点 Counter Server 提供计数功能,接收客户端发起的计数请求和查询请求。

640?wx_fmt=png图5 - 架构 1.0

但是这样的架构设计存在这样两个问题:一是 Server 是一个单点,一旦 Server 节点故障服务就不可用了;二是运算结果都存储在内存当中,节点故障会导致数据丢失。

640?wx_fmt=png图6 - 架构 1.0 的不足:单点

针对第二个问题,我们优化一下,加一个本地文件存储。这样每次计数器完成运算之后都将数据落盘,当节点故障之时,我们要新起一台备用机器,将文件数据拷贝过来,然后接替故障机器对外提供服务。这样就解决了数据丢失的风险,但是同时也引来另外的问题:磁盘 IO 很频繁,同时这种冷备的模式也依然会导致一段时间的服务不可用。

640?wx_fmt=png图7 - 架构 1.0 的不足:冷备

所以我们提出架构 2.0,采用集群的模式提供服务。我们用三个节点组成集群,由一个节点对外提供服务,当 Server 接收到 Client 发来的写请求之后,Server 运算出结果,然后将结果复制给另外两台机器,当收到其他所有节点的成功响应之后,Server 向 Client 返回运算结果。

640?wx_fmt=png图8 - 架构 2.0

但是这样的架构也存在这问题:

  • 我们选择哪一台 Server 扮演 Leader 的角色对外提供服务;

  • 当 Leader 不可用之后,选择哪一台接替它;

  • Leader 处理写请求的时候需要等到所有节点都响应之后才能响应 Client;

  • 也是比较重要的,我们无法保证 Leader 向 Follower 复制数据是有序的,所以任一时刻三个节点的数据都可能是不一样的;

保证复制数据的顺序和内容,这就有了共识算法的用武之地,所以在接下来的 3.0 架构里,我们使用 SOFAJRaft 来助力集群的实现。

640?wx_fmt=png图8 - 架构 3.0:使用 SOFAJRaft

3.0 架构中,Counter Server 使用 SOFAJRaft 来组成一个集群,Leader 的选举和数据的复制都交给 SOFAJRaft 来完成。在时序图中我们可以看到,Counter 的业务逻辑重新变得像架构 1.0 中一样简洁,维护数据一致的工作都交给 SOFAJRaft 来完成,所以图中灰色的部分对业务就不感知了。

640?wx_fmt=png图9 - 架构 3.0:时序图

在使用 SOFAJRaft 的 3.0 架构中,SOFAJRaft 帮我们完成了 Leader 选举、节点间数据同步的工作,除此之外,SOFAJRaft 只需要半数以上节点响应即可,不再需要集群所有节点的应答,这样可以进一步提高写请求的处理效率。

640?wx_fmt=png图10 - 架构 3.0:SOFAJRaft 实现 Leader 选举、日志复制

| 使用 SOFAJRaft

那么怎么使用 SOFAJRaft 呢?我们之前说过,SOFAJRaft 主要暴露了两个地方给我们去实现,一是 Cilent,另一个是 StateMachine,所以我们的计数器也就是要去做这两部分。

在 Client 上,我们要定义具体的消息类型,针对不同的消息类型,还需要去实现消息的 Processor 来处理这些消息,接下来这些消息就交给 SOFAJRaft 去完成集群内部的数据同步。

在 StateMachine 上,我们要去实现状态机暴露给我们待实现的几个接口,最重要的是 onApply 接口,要在这个接口里将 Cilent 的请求指令进行运算,转换成具体的计数器值。而 onSnapshotSave 和 onSnapshotLoad 接口则是负责快照的生成和加载。

640?wx_fmt=png图11 - 模块关系

下面这张图是最终实现的模块关系图,其实他已经是代码实现之后的产物了,在这里并没有贴出具体的代码,因为代码已经随我们的项目一起开源了。

我们实现了两种消息类型 IncrementAndGetRequest 和 GetValueRequest,分别对应写请求和读请求,因为两种请求的响应都是计数器的值,所以同用一个 ValueResponse。

两种请求,所以对应两种 Processor:IncrementAndGetRequestProcessor 和 GetValueRequestProcessor。

状态机 CounterStateMachine 实现了之前提到的三个接口,除此之外还实现了 onLeaderStart 和 onLeaderStop,用来在节点成为 leader 和失去 leader 资格时做一些处理。这个地方在写请求的处理中使用了 IncrementAndAddClosure ,这样就可以通过 callback 的方式来实现响应。

640?wx_fmt=png图12 - 类关系图

| 启动运行

来看看整个的启动过程。首先来看 Follower 节点的启动 (当然,在启动之前,我们并不知道哪个节点会是 Leader),Counter 在本地起三个进程用来模拟三个节点,它们分别使用 8081、8082、8083 三个端口,标记其为 A、B、C 节点。

A 节点率先启动,然后开始向 B 和 C 发送 preVote 请求,但是这时候另外两个节点都尚未启动,所以 A 节点通信失败,然后等待,再重试,如此往复。在 A 节点某次通信失败后的等待之中,它突然收到了 B 节点发来的 preVote 请求,在经过一系列 check 之后,它认可了这个 preVote 请求,并且返回成功响应,随后又对 B 节点发来的 vote 请求成功响应,然后我们可以看到,B 节点成功当选 Leader。这就是 Follower A 的启动、投票过程。

640?wx_fmt=png图13 - Follower 启动日志

我们再看看 B 节点的启动,B 节点在启动之后,刚好处于 A 节点的一次等待间隙之中,所以它没有收到其他节点发来的 preVote 请求,因此它向另外两个节点发起了 preVote 请求,试图竞选。接下来它收到了 A 节点发来的确认响应,接着 B 节点又发起了 vote 请求,依然收到了 A 节点的响应。这样 B 节点就收到了超过集群半数以上的投票并成功当选 (A 节点和 B 节点自己,达到 2/3) 。在此过程中,C 节点一直没有启动,但是由于 A 和 B 构成半数以上,所以共识算法已经可以正常 work。

640?wx_fmt=png图14 - Leader 启动日志

在刚才的过程中,我们提到了两个关键词:preVote 和 vote,这是选举中的两个阶段,之所以要设置 preVote,是为了应对网络分区的情况。关于 SOFAJRaft 的选举[1],我们有专门的文章去解析 ,大家可以进一步了解。在这里我们将选举的评选原则粗略的描述为:哪个节点保存的日志最新最完整,它就更有资格成为 leader。

接下来我们看看 Client 发起的一次写请求。Client 共发起了三次写请求,分别是 "+0"、"+1"、"+2"。从日志上我们可以看到,Leader 在收到这些请求之后,先把他们以日志的形式发送给其他节点 (并且是批量的),当它收到其他节点对日志复制的成功响应之后,再更新 committedIndex,最后调用 onApply 接口,执行 counter 的计数运算,将 client 发来的指令加到计数器当中。在这个过程中,可以看到 Leader 在处理写请求的时候一个很重要的步骤就是将日志复制给其他节点。来详细看下这个过程,以及当中提到的 committedIndex。

640?wx_fmt=png图15 - Leader 处理写请求

CommittedIndex 标志了一个位点,它标志在此之前的所有日志都已经复制到了集群半数以上的节点之中。图中可以看到,committedIndex 初始指在 "3" 这个位置上,表示 "0-3" 的日志都已经复制到了半数以上节点之中 (在 Follower 上我们也已经看到),接下来 Leader 又把 "4"、"5" 两条日志批量的复制到了 Follower 上,这是就可以把 committedIndex 右滑动到 "5" 的位置,表示 "0-5" 的日志都已经复制到了半数以上节点之中。

640?wx_fmt=png图16 - 日志复制

这时又产生了另一个问题:我们如何知道 StateMachine 执行到哪一条日志了?通过 committedIndex 我们可以知道哪些日志已经成功复制到集群其他节点之中了,但是 StateMachine 中此刻的状态代表哪一条日志执行之后的结果呢?这就要用 applyIndex 来表示。

在图中,applyIndex 指向 "3",这表示:"0-3" 的日志代表的指令都已经被 StateMachine 执行,状态机此刻的状态代表 "3" 日志执行完毕之后的结果,当 committedIndex 向右滑动之后,applyIndex 就可以伴随状态机的执行继续向右滑动了。ApplyIndex 和 committedIndex 就可以支持线性一致性读[2],关于这个概念,我们也已经有文章去专门解析了,可以在文末链接中了解。

640?wx_fmt=png图17 - ApplyIndex 更新

| 小结

今天以 Counter 为例,先介绍了 SOFAJRaft 的概念,然后从需求提出开始,一步步完善架构,明确业务要实现哪些接口,最后启动日志观察 SOFAJRaft 如何支撑业务执行。在此过程中涉及到了一些 SOFAJRaft 的在直播中没有继续深入的概念,也给出了相关的解析文章。

如果大家对于 Counter 例子[3]还想要有更多的了解,欢迎在官网浏览相关文章 ,或者在项目中查看具体代码[4]。

文中提到的相关链接

[1] SOFAJRaft 选举机制剖析 | SOFAJRaft 实现原理:https://www.sofastack.tech/blog/sofa-jraft-election-mechanism/[2] SOFAJRaft 线性一致读实现剖析 | SOFAJRaft 实现原理:https://www.sofastack.tech/blog/sofa-jraft-linear-consistent-read-implementation/[3] Counter 例子详解:https://www.sofastack.tech/projects/sofa-jraft/counter-example/[4] GitHub example:https://github.com/sofastack/sofa-jraft/tree/master/jraft-example[5] SOFAJRaft 源码解析系列:https://www.sofastack.tech/categories/sofajraft/[6] SOFAJRaft GitHub:https://github.com/sofastack/sofa-jraft/

本期视频回顾以及 PPT 查看地址

https://tech.antfin.com/community/live/821

往期直播精彩回顾

  • 探秘金融级云原生发布工作负载 CafeDeployment:

    https://tech.antfin.com/community/live/737

  • 蚂蚁金服轻量级监控分析系统解析 | SOFAChannel#6 直播整理:

    https://tech.antfin.com/community/live/687

  • 给研发工程师的代码质量利器 | SOFAChannel#5 直播整理:

    https://tech.antfin.com/community/live/552

  • 分布式事务 Seata TCC 模式深度解析 | SOFAChannel#4 直播整理:

    https://tech.antfin.com/community/live/462

  • SOFARPC 性能优化实践(下)| SOFAChannel#3 直播整理:

    https://tech.antfin.com/community/live/245

  • SOFARPC 性能优化实践(上)| SOFAChannel#2 直播整理:

    https://tech.antfin.com/community/live/244

  • 从蚂蚁金服微服务实践谈起 | SOFAChannel#1 直播整理:

    https://tech.antfin.com/community/live/148

本文归档在 sofastack.tech。

640?wx_fmt=png

这篇关于从一个例子开始体验 SOFAJRaft | SOFAChannel#8 直播整理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/614950

相关文章

数论入门整理(updating)

一、gcd lcm 基础中的基础,一般用来处理计算第一步什么的,分数化简之类。 LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } <pre name="code" class="cpp">LL lcm(LL a, LL b){LL c = gcd(a, b);return a / c * b;} 例题:

速盾:直播 cdn 服务器带宽?

在当今数字化时代,直播已经成为了一种非常流行的娱乐和商业活动形式。为了确保直播的流畅性和高质量,直播平台通常会使用 CDN(Content Delivery Network,内容分发网络)服务器来分发直播流。而 CDN 服务器的带宽则是影响直播质量的一个重要因素。下面我们就来探讨一下速盾视角下的直播 CDN 服务器带宽问题。 一、直播对带宽的需求 高清视频流 直播通常需要传输高清视频

rtmp流媒体编程相关整理2013(crtmpserver,rtmpdump,x264,faac)

转自:http://blog.163.com/zhujiatc@126/blog/static/1834638201392335213119/ 相关资料在线版(不定时更新,其实也不会很多,也许一两个月也不会改) http://www.zhujiatc.esy.es/crtmpserver/index.htm 去年在这进行rtmp相关整理,其实内容早有了,只是整理一下看着方

笔记整理—内核!启动!—kernel部分(2)从汇编阶段到start_kernel

kernel起始与ENTRY(stext),和uboot一样,都是从汇编阶段开始的,因为对于kernel而言,还没进行栈的维护,所以无法使用c语言。_HEAD定义了后面代码属于段名为.head .text的段。         内核起始部分代码被解压代码调用,前面关于uboot的文章中有提到过(eg:zImage)。uboot启动是无条件的,只要代码的位置对,上电就工作,kern

JavaScript整理笔记

JavaScript笔记 JavaScriptJavaScript简介快速入门JavaScript用法基础语法注释关键字显示数据输出innerHTML innerText属性返回值的区别调试 数据类型和变量数据类型数字(Number)字符串(String)布尔值(Boolean)null(空值)和undefined(未定义)数组(Array)对象(Object)函数(Function) 变量

JavaFX环境的搭建和一个简单的例子

之前在网上搜了很多与javaFX相关的资料,都说要在Eclepse上要安装sdk插件什么的,反正就是乱七八糟的一大片,最后还是没搞成功,所以我在这里写下我搭建javaFX成功的环境给大家做一个参考吧。希望能帮助到你们! 1.首先要保证你的jdk版本能够支持JavaFX的开发,jdk-7u25版本以上的都能支持,最好安装jdk8吧,因为jdk8对支持JavaFX有新的特性了,比如:3D等;

javaScript日期相加减例子

当前时间加上2天 var d = new Date(“2015-7-31”); d.setDate(d.getDate()+2); var addTwo=d.getFullYear()+”年”+(d.getMonth()+1)+”月”+d.getDate()+”日”; “控制台输出===============”+”当前日期加2天:”+addTwo; 使用这种方法,月份也会给你计算.

关于回调函数和钩子函数基础知识的整理

回调函数:Callback Function 什么是回调函数? 首先做一个形象的比喻:   你有一个任务,但是有一部分你不会做,或者说不愿做,所以我来帮你做这部分,你做你其它的任务工作或者等着我的消息,但是当我完成的时候我要通知你我做好了,你可以用了,我怎么通知你呢?你给我一部手机,让我做完后给你打电话,我就打给你了,你拿到我的成果加到你的工作中,继续完成其它的工作.这就叫回叫,手机

站长常用Shell脚本整理分享(全)

站长常用Shell脚本整理分享 站长常用Shell脚本整理分享1-10 站长常用Shell脚本整理分享11-20 站长常用Shell脚本整理分享21-30 站长常用Shell脚本整理分享31-40 站长常用Shell脚本整理分享41-50 站长常用Shell脚本整理分享51-59 长期更新

设计模式大全和详解,含Python代码例子

若有不理解,可以问一下这几个免费的AI网站 https://ai-to.cn/chathttp://m6z.cn/6arKdNhttp://m6z.cn/6b1quhhttp://m6z.cn/6wVAQGhttp://m6z.cn/63vlPw 下面是设计模式的简要介绍和 Python 代码示例,涵盖主要的创建型、结构型和行为型模式。 一、创建型模式 1. 单例模式 (Singleton