专栏首页分布式架构一致性算法 - Raft协议实践(SOFAJRaft剖析)
原创

一致性算法 - Raft协议实践(SOFAJRaft剖析)

- SOFAJRaft 概述 -

咱们对Raft协议已经进行了原理的解析,接下去咱们从通过SOFAJRaft 框架的核心流程剖析加深对Raft协议的理解。SOFAJRaft 是一个纯 Java 的 Raft 算法实现库, 基于百度 braft 实现而来, 使用 Java 重写了所有功能, 支持:

  • 领导人选举和基于优先级的半确定性领导人选举。
  • 日志复制和恢复。
  • 快照和日志压缩。
  • 只读成员(learner)。
  • 集群成员管理,添加节点,删除节点,替换节点等。
  • 完全并发复制。
  • 容错能力。
  • 非对称网络分区容忍性。
  • 当法定人数同伴都死亡的解决方法。
  • 管道复制
  • 线性一致读,ReadIndex/LeaseRead。

额外扩展了一些功能:

  • 对称网络分区容忍性
  • 重启后的转移领袖、负载均衡场景实现
  • 更丰富的指标统计展示
  • 通过Jepsen一致性验证测试
  • 包含嵌入式分布式KV存储实现

整体项目如下:

- 领袖选举 -

SOFAJRaft 的选举主要通过判单两个属性:LogIndex 和 Term;Term 即任期,LogIndex即提交到 raft group 中的任务都将序列化为一条日志存储下来,每条日志一个编号,在整个 raft group 内单调递增并复制到每个 raft 节点。可以理解为事务id。投票处理的逻辑主要在 com.alipay.sofa.jraft.core.NodeImpl中,主要有四个函数:

  • 处理处理预投票请求 Message handlePreVoteRequest(request)
  • 预投票 void preVote()
  • 处理投票请求 Message handleRequestVoteRequest(request)
  • 投票 electSelf()

整体流程如下:

  • Candidate(候选人) 被 Election timeout触发
  • Candidate 开始尝试发起 pre-vote 预投票
  • Follower(追随者) 判断是否认可该 pre-vote request
  • Candidate 根据 pre-vote response 来决定是否发起 RequestVoteRequest
  • Follower 判断是否认可该 RequestVoteRequest
  • Candidate 根据 response 来判断自己是否当选

使用预投票可以防止网络抖动等特殊原因引起的瞬时失联节点无故捣乱:候选者在发起投票之前,先发起预投票,如果没有得到半数以上节点的反馈,则候选者就会识趣的放弃参选,也就不会抬升全局的 Term。

投票源码:

预投票源码:

- 存储机制 -

SOFAJRaft 存储模块分为:

  • Log 存储记录 Raft 配置变更和用户提交任务日志,把日志从 Leader 复制到其他节点上面;
    • LogStorage 是日志存储实现,默认实现基于 RocksDB 存储,通过 LogStorage 接口扩展自定义日志存储实现;核心接口包括:
      • 返回日志里的首/末个日志索引;
      • 按照日志索引获取 Log Entry 及其任期;
      • 把单个/批量 Log Entry 添加到日志存储;
      • 从 Log 存储头部/末尾删除日志;
      • 删除所有现有日志,重置下任日志索引。
    • LogManager 负责调用底层日志存储 LogStorage,针对日志存储调用进行缓存、批量提交、必要的检查和优化。
      • checkAndResolveConflict(entries, done)
        1. 检查Node节点,解决日志冲突。
        2. 配置管理器:缓存配置变更
        3. LogsInMemory缓存日志Entries
      • offerEvent(done, type) Disruptor队列发布other类型事件
      • appendToStorage(toAppend) 回调事件处理器StableClosureEventHandler存储日志
  • Meta 存储即元信息存储记录 Raft 实现的内部状态,比如当前 term,、投票给哪个节点等信息
    • RaftMetaStorage 元信息存储实现,定义 Raft 元数据的 Metadata 存储模块核心 API 接口包括:
      • 设置/获取 Raft 元数据的当前任期 Term;
      • 分配/查询 Raft 元信息的 PeerId 节点投票。
  • Snapshot 存储用于存放用户的状态机 Snapshot 及元信息,用于Node重启重建整个状态机实例。
    • SnapshotStorage 用于 snapshot 存储实现,定义 Raft 状态机的 Snapshot 存储模块核心接口包括:
      • 设置 filterBeforeCopyRemote ,为 true 表示复制到远程之前过滤数据;
      • 创建快照编写器;
      • 打开快照阅读器;
      • 从远程 Uri 复制数据;
      • 启动从远程 Uri 复制数据的复制任务;
      • 配置 SnapshotThrottle,SnapshotThrottle 用于重盘读/写场景限流的,比如磁盘读写、网络带宽。
    • SnapshotExecutor 用于 snapshot 实际存储、远程安装、复制的管理。
      • 状态机快照 doSnapshot(done)
      • 安装快照 installSnapshot(request, response, done)。

LogManager 调用日志存储 LogStorage 实现逻辑:

SnapshotExecutor 状态机快照和远程安装镜像实现逻辑:

- 一致性状态机 -

通过存储的设计,在引入状态机机制,就可以完成一致性状态机。SOFAJRaft状态机组成有:

  • StateMachine:业务逻辑实现的主要接口,状态机运行在每个 raft 节点上,提交的 task 如果成功,最终都会复制应用到每个节点的状态机上。,核心是 onApply(Iterator) 方法,应用通过 Node#apply(task) 提交的日志到业务状态机。
  • FSMCaller:封装对业务 StateMachine 的状态转换的调用以及日志的写入等,一个有限状态机的实现,做必要的检查、请求合并提交和并发处理等。

SOFAJRaft Node节点利用日志复制完成数据同步,主要组成有:

  • Replicator:用于 leader 向 follower 复制日志,也就是 raft 中的 appendEntries 调用,包括心跳存活检查等。
  • ReplicatorGroup: 用于单个 RAFT Group 管理所有的 replicator,必要的权限检查和派发。
- 总结 -

本文通过简单介绍了下SOFAJRaft的选举实现、存储机制、状态机和日志复制四个方面。基本上完成了Raft实现的核心实现。但SOFAJRaft还有更多核心及优化,因为篇幅原因没有进入细细剖析。如果咱们自实现Raft协议,基本上也是实现这几个主流程即可完成简版Raft了。关于Raft协议暂时先告一段路,接下去准备开写ZAB协议。

- 作者介绍 -

林淮川

毕业于西安交通大学;奈学教育《百万架构师训练营》讲师、企业级源码内源负责人,前大树金融高级架构师、技术委员会开创者、技术总监;前天阳宏业交易事业部技术主管;多年互联网金融行业(ToB)经验。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 一致性算法 - Raft协议流程

    咱们上文整体的介绍了下Raft协议,Raft协议分区容忍的一致性协议的核心思想:一致性的保证不一定非要所有节点都保持一致,只要大多数节点更新了,对于整个分布式系...

    林淮川
  • 分布式架构设计篇(十一)-柔性事务之最大努力通知事务详解

    ​ 咱们在上一篇文章探讨了事务消息,事务消息是基于MQ实现的一种异步事务。接下来咱们开始聊聊咱们分布式事务系列中的最后一个方案:最大努力通知事务。最...

    林淮川
  • 分布式架构设计篇(七)-刚性事务总结和柔性事务概述

    ​ 在《分布式架构之设计篇-刚性事务之2PC详解》和《分布式架构之设计篇-刚性事务之3PC详解》二文中分析了分布式事务的本质、XA、2PC、3PC等...

    林淮川
  • SpringBoot基础篇日志管理之logback配置文件

    也不针对语法进行逐一说明了,直接针对常见的xml配置文件进行分析,反正看完也会忘的,等到要用的时候,一般都是拷贝一个魔板,改改名啥的就over了

    一灰灰blog
  • 日志使用规范

    【强制】日志系统调用方式如下:         import org.slf4j.Logger;         import org.slf4j.Logg...

    用户1409099
  • 计算机基础知识_硬件知识

      硬件知识 计算机之父(冯·诺依曼)提出了一种结构 电子管-晶体管-集成电路-大规模集成电路 主要的要点是: 1.)计算机存储是按照二进制存储的 作用是 可...

    IBinary
  • 关于laravel 日志写入失败问题汇总

    项目部署到Linux 服务器上后有时会出现 每日日志无法写入的问题。由此汇总一下常出现的问题及解决方式。

    砸漏
  • 深度揭秘腾讯云数据库技术7年变迁史

    早在上个世纪 50、60 年代,“数据”二字就已不再是简单的数字信息而已。随着信息技术的不断发展,在计算机应用领域,计算机存储和处理的对象逐渐广泛,表示这些对象...

    腾讯云数据库 TencentDB
  • 事务日志还原的次意外的操作失误

    前几天新发布的一套程序发生了一次意外的操作失误,程序员修改某个表的指定范围的指定字段的时候,误操作导致更新操作没有执行where,直接引起该表指定字段全部变为更...

    用户1217611
  • phpMyAdmin利用日志文件GetSHELL

    phpMyAdmin 是众多MySQL图形化管理工具中使用最为广泛的一种,是一款使用PHP 开发的基于B/S模式的MySQL客户端软件,该工具是基于 Web 跨...

    渗透攻击红队

扫码关注云+社区

领取腾讯云代金券