dledger原理源码分析(四)-日志

news2024/11/15 15:53:08

简介

dledger是openmessaging的一个组件, raft算法实现,用于分布式日志,本系列分析dledger如何实现raft概念,以及dledger在rocketmq的应用

本系列使用dledger v0.40

本文分析dledger的日志,包括写入,复制,共识点推进

关键词

Raft

Openmessaging

参考资料

In Search of an Understandable Consensus Algorithm  raft论文简版

技术架构

  • 应用/client  client是dledger提供给应用访问节点的组件

以下是节点内组件

  • rpc服务

rpc服务内置rpc client/rpc server,对外接收外部rpc访问,包括client和节点间通讯;对内,解释rpc请求,转发给Server;对外,发送rpc请求到其他节点

  • Server

主程序,负责节点启动,其他组件的启动;写入日志请求初步处理等

  • Elector

选举类,负责集群主节点选举

  • EntryPusher

日志写入器,内置分发器和处理器,分发器主节点用于复制日志到跟随者;处理器跟随者使用,写入日志

  • 存储

存储日志条木,有两个实现,基于内存和基于文件

  • 快照/状态机

新版本的dledger提供状态机,dledger成为通用的raft组件,不再是转为rocketmq使用

日志

技术架构

日志组件核心是维护共识日志,所有节点都认可的日志记录点。应用发起写入rpc请求,需领导者处理,写入本地存储,领导者复制到其他节点,不断推进日志共识点。集群领导者下线,其他节点使用共识点恢复集群

  • EntryPusher 统筹日志复制全局的角色,为领导者构建跟随者对应的EntryDispatcher,负责跟随者的同步,为每个跟随者构建EntryHandler处理同步请求
  • 集群领导者通过比对/截取/写入/提交 同步日志
  • QuorumAckChecker全局提交,推进共识

下面详细分析各参与组件

领导者写入

应用写入日志需要由领导者处理,领导者写入消息到本地存储后,复制到跟随者

写入请求DLedgerServer的handleAppend方法处理,然后appendAsLeader写入存储,最后DLedgerEntryPusher的appendClosure方法,登记本节点的日志水位线,pending返回

复制

领导者写入日志,揭开集群同步的序幕

领导者为每个跟随者构建EntryDispatcher,负责该跟随者的同步,QuorumAckChecker全局提交,确定共识index

EntryDispatcher是有状态,定时驱动

上图展示EntryDispatcher状态变更,状态变更执行的动作,相对应的EntryHandler处理方法,当然,实际不是直接交互的,EntryHandler缓存请求,定时处理,后面章节详细分析

其中,

INSTALL_SNAPSHOT 放到后续存储/快照中分析,在本版本,快照可选,没有该功能不影响日志复制

COMMIT  EntryDispatcher没有处在该状态,只是一个操作

关键属性

很大程度上,理解dledger的日志复制就是理解日志存储的位置点和位置点动态变化,本周介绍一下关键属性

下图展示日志关键index

 DLedgerEntryPusher

  • peerWaterMarksByTerm

定义:Map<Long/*term*/, ConcurrentMap<String/*peer id*/,  Long/*match index*/>>

term--->peerId--->matchIndex

按term记录每个节点的日志水位(复制进度)

  • pendingClosure

定义:Map<Long /*term*/,  ConcurrentMap<Long /*index*/,  Closure /*upper callback*/>>

term--->index---> closure

按term存放消息索引点的返回

EntryDispatcher
  • writeIndex

跟随者同步的下一个写入点

  • matchIndex

比对和截取状态:比对匹配的日志index,用于后面截取

写入状态:

  • pendingMap

定义:ConcurrentMap<Long/*index*/, Pair<Long/*send timestamp*/, Integer/*entries count in req*/>>

记录写入请求,以写入点位key,

  • batchAppendEntryRequest

缓存写入消息请求,批量推送到跟随者

​​​​​​​MemberState
  • committedIndex

集群日志写入位置共识,过半节点日志index

  • ledgerEndIndex

最近写入日志index

  • ledgerEndTerm

最近写入日志index所在的term

  • appliedIndex

用于状态机,后续状态机/快照分析

DLedgerStore
  • beforeBeginIndex

存储启动/恢复,未写入新消息前的位置,该index指向启动时最新一个消息

  • ledgerEndIndex

MemberState相同

比对

领导者上任或与跟随者同步写入出现不一致时,EntryDispatcher进入比对状态,找到跟随者与领导者日志的匹配点,即,最大的term/index一致

  • 比对请求

1 检查是否为领导者,是否变更为领导者

2 ledgerEndIndex=-1,主节点没有写入日志

严格来说不是没有写入,是只有beforeBegin写入

3 从跟随者最近已写入点开始比对

注意:这个writeIndex是代表跟随者,初始是领导者本地的ledgerEndIndex,经过一轮比对,返回跟随者的ledgerEndIndex

4 compareIndex < beforeBeginIndex

说明跟随者日志存在较大落后,通过快照恢复到beforeBeginIndex,比对操作才介入

-----------------------------------------分割线----------------------------------------

5 compareIndex = beforeBeginIndex

这是特殊的index,dLedgerStore存有index和term,其他的需要从存储获取消息条目(Entry),但存储只支持获取大于beforeBeginIndex的消息

6 compareIndex > beforeBeginIndex 正常

正常消息从存储获取,从而获得term,构建比对请求

7 推送比对请求到跟随者

8 等待返回

9 比对成功

9.1 设置matchIndex,更新跟随者的水位线

9.2 比对完成,转到截取

10 返回不一致状态,需继续比对,writeIndex设置为跟随者返回的可比较点

10.1 term不一致

10.2 跟随者返回的最近写入index,用于下一轮比对

参看处理对比

  • 处理比对

0 获取比对的日志index,term

1 检查,排除刚开始没有任何日志的情况

2 本地最近写入点ledgerEndIndex>=preLogIndex

若ledgerEndIndex < preLogIndex,比对的存储还没写入

2.1 beforeBeginIndex是特殊的index

上面比对请求介绍过

2.2 其他index需要从存储获取消息来获取term

2.3 匹配条件是term&index

2.4 term不一样,返回本节点term的第一个写入的index

返回的index,领导者减一作为下一个比较点,实际退到上一个term最后写入index比对,为什么这样做?

我理解,另一个做法是index-1,再比对,同一个index,领导者的term与自身term不一致,说明跟随者之前是领导者,或者是写入比当前领导者多的节点,而term期限内,一般写入的消息比较多,若index减1再比较,可能经过很多轮次的比较才获得匹配点,很难预计多少轮才找到匹配点,不如退到上一个term最后写入点比较快

-----------------------------------------分割线----------------------------------------

3 这里有点偷懒的嫌疑,个人认为不应该定为不一致

截取

比对完成,得到匹配点matchIndex,matchIndex后面截掉,同步写入从matchIndex+1开始

  • 截取请求

 截取请求比较简单,不多解释

  • 处理截取

 1 截取存储

1.1 截取存储后,返回index是ledgerEndIndex,truncateIndex是第一个截取点,因此截取后正常情况index=truncateIndex-1

2 修改committedIndex,1.1 截取更新了ledgerEndIndex

committedIndex/ledgerEndIndex 取小的更新committedIndex,初看起来感到困惑,这样意味着共识点会后退,但回想 5.3.3 比对处理比对 “2.4 term不一样,返回本节点term的第一个写入的index”, 回退到上一个term,可能出现大量的消息截取,出现共识点回退,但这个也是取舍。我个人不太认可,成为共识,继续往前推

写入(append)

经过比对和截取,领导者的EntryDispatcher与跟随者找到日志匹配点(mathIndex),EntryDispatcher从匹配点推送日志给跟随者

  • 写入请求

1 是否leader, 是否变更为leader,leader变更转为COMPARE状态

2 检查pending写入请求是否有超时,超时清理

下面分析一下doCheckAppendResponse

检查pending写入请求是否超时,检查第一个,最有可能超时的请求

2.1/2.2  最近写入水位线+1,就是第一个pending请求key

2.3 未发送,当前batchAppendEntryRequest是第一个pending写入请求

2.4 超过推送返回时间,需重新发送

此时批量请求存放的是下一批(可能是第二,第三批)的待发送请求,需要重发前一批,清理,重置writeIndex为第一批index,

注意:这里调整了writeIndex,pendingMap没有改变,后面将从writerIndex构建请求,形成两条请求线,这对整个写入理解很关键,将在5中详细分析

----------------------------------------------分割线------------------------------------------------

3  writeIndex>ledgerEndIndex, 跟随者写入index>领导者最后写入index,没有消息可复制

writeIndex等于ledgerEndIndex+1,复制到最新的写入,下一个复制点加1

3.1  写入文档有未推送,推送后批量请求的第一个index为key,缓存请求到pendingMap,并清理请求,

推送请求的异步处理只处理SUCCESS,INCONSISTENT_STATE两个返回状态, 也就是说,其他网络超时,参数重复不对pendingMap处理

3.2 空闲,提交

4 到这里,writeIndex <= ledgerEndIndex

  进一步细分 writeIndex <= beforeBeginIndex

  存储只能获取大于beforeBeginIndex,只有通过快照恢复到大于beforeBeginIndex

----------------------------------------------分割线------------------------------------------------

下面分析清理pengdingMap中的请求,首先分析一下pendingMap的状态

在2.4 分析过,pengding请求超时,writeIndex调回到水位线+1作为写入点,

>上图的上部分是初始状态

writeIndex1 第一个批次请求;writeIndex2第二个批次请求,并已发送,pendingMap有两个数据

>上图的下部分,writeIndex1超时,调和writeIndex,请求重新发送,但批次数量会有所不同,形成新的writeIndex1(覆盖原有),writeIndex2-x,而且writeIndex1的批量比原writeIndex1大,writeIndex2-x比原writeIndex2大,pendingMap有3个数据

此时有两条发送线,不能说writeIndex2已废弃,可能跟随者已处理了原writeIndex1,只是回复失败,处理写入分析跟随者怎样处理

通过上面pendingMap的状态分析,很容易理解清理的逻辑,

第一个条目index+条目数-1 = 最后条目的index

最后条目的index < 写入水位线 就是上面pendingMap分析图,上部分的writeIndex2

当跟随者写入的是第二条线,writeIndex2-x完成后

----------------------------------------------分割线------------------------------------------------

6  pengding请求数量是否超限

本人觉得这里再做一个过期清理无必要,下一轮开始就会过期清理

7  pengdingMap缓存请求没有超,writeIndex消息条目加入批量请求,条件达到可发送批量请求

8  下一个写入点

  • 处理写入

  下图是doWork 写入部分

写入请求与其他TRUNCATE/COMPARE/COMMIT请求不同,其他的请求是缓存再队列,写入请求是map,已写入的index为key,这也是保证写入完整性的关键

1 写入点,ledgerEndIndex+1

2/3 下一个请求,请求空,请求未到或者写入了旧的请求线,参看pengdingMap分析

接着是检查请求Map

----------------------------------------------分割线------------------------------------------------

3.1.1 处理有点不解,按pendingMap分析,writeIndex2批量可能被返回不一致,处理没错,但本人认为更好的处理应该是保证写入点连续性即好,过期的请求删去就可以,返回不一致,领导者进入比对,降低效率

----------------------------------------------分割线------------------------------------------------

3.4 我理解,3.1处返回不一致,领导者转入COMPARE,跟随者也会转到COMPARE,继而TRUNCATE状态,writeRequestMap被清理

3.5 纵观整个清理逻辑,核心是及早发现请求的不连续性

minFastForwardIndex是不连续的请求写入点,因为出现写入点在3.2已退出

----------------------------------------------分割线------------------------------------------------

跟随者日志写入,这里值得注意的是,请求带了cimmittedIndex,主要作用孜孜不倦地推进写入落后的跟随者靠近或者达到共识点,非常好的细节!

提交

提交是EntryDispatcher的一个状态,但EntryDispatcher没有被赋予该状态,因此提交不是定时执行,而是写入(append)在空闲时调用提交,推进跟随者的日志

  • 提交请求

提交比较简单,从MemberState获取committedIndex,构建请求,推送到跟随者

  • 处理写入

committedIndex/ledgerEndIndex取小的,参看5.3.7 Quorum检查分析,committedIndex是过半数的日志水位线,有些跟随者的ledgerEndIndex比committedIndex,同步写入的进度慢

Quorum检查

Quorum法定人数,这个数值应该是可以设置,超过半数都可以,甚至可以设为总数,但dledger实际操作按过半数,我们这里也按过半数理解

              前面的比对,截取,写入不断复制日志到跟随者,QuorumChecker定时检查,记录阶段的共识点,然后通过”5.3.6提交”, 提交共识点到跟随者,集群就是这样持续的向前推进

    QuorumChecker前面清理工作不详细分析

5 检查是否未领导者

6 更新自身(领导者)的水位线

7 计算集群共识点

8 更新到MemberState

新领导者

新领导者上任首要是恢复共识日志,dledger使用Raft Log Read恢复集群日志共识,Raft Log Read是客户端一致性读取分布式日志的技术,其原理是client伪装成集群节点,走一遍Raft Log,经过前面分析,很容易想到实现原理, 3板斧-比对,截取,写入,其中,如果比对index < beforeBeginIndex先恢复快照

线性一致读

dledger实现了Raft Log Read,目前用于新领导者恢复共识日志,但其性能开销应用不能接受,ReadIndex Read/Lease Read预留了方法但还未实现, 因此dledger未能用于开发对外应用,例如,kv。

jraft实现了ReadIndex Read/Lease Read,如想了解线性一致读可以了解一下jraft

系列文章

  • 架构,核心组件和rpc组件 完成
  • 心跳和选举 完成
  • 日志 完成
  • 存储和快照/状态机 TBD  早期dledger没有快照和状态机,使其成为通用的raft组件迈进一大步
  • 集成rocketmq  rocketmq使用dledger实现消息存储复制;broker控制器 元数据复制
  • 集群成员变更  成员变更管理,节点的上线下线发现,dledger v0.4未实现,但计划中,未来版本实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1915196.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件架构之架构风格

软件架构之架构风格 9.3 软件架构风格9.3.1 软件架构风格分类9.3.2 数据流风格9.3.3 调用/返回风格9.3.4 独立构件风格9.3.5 虚拟机风格9.3.6 仓库风格 9.4 层次系统架构风格9.4.1 二层及三层 C/S 架构风格9.4.2 B/S 架构风格9.4.3 MVC 架构风格9.4.4 MVP 架构风格 9.5 面向服务…

力扣 双指针基础

class Solution {public void moveZeroes(int[] nums) {int l 0;//慢指针但先走for (int r 0; r < nums.length; r) {//快指针&#xff0c;遍历次数if (nums[r] 0) continue;//l比r先到&#xff0c;在此处定住l&#xff0c;r继续移动int t nums[l];nums[l] nums[r];num…

上交发布MG-LLaVA,基于多粒度指令调整,横扫视觉大模型榜单

近年来多模态大语言模型(MLLMs)在视觉理解任务中取得了长足进步。然而&#xff0c;大多数模型仍局限于处理低分辨率图像&#xff0c;这限制了它们在需要详细视觉信息的任务中的表现。针对这一问题&#xff0c;上海交通大学的研究团队推出了MG-LLaVA&#xff08;Multi-Granulari…

Animate软件基础:重命名图层或文件夹

默认情况下&#xff0c;Animate 会按照创建顺序向新图层分配名称&#xff1a;图层 1、图层 2&#xff0c;依此类推。为了更好地反映图层的内容&#xff0c;可以对图层进行重命名。 如果需要对图层或图层文件夹进行重命名&#xff0c;请执行下列操作之一&#xff1a; 双击时间轴…

二叉平衡树(左单旋,右单旋,左右双旋、右左双旋)

一、AVL树&#xff08;二叉平衡树&#xff1a;高度平衡的二叉搜索树&#xff09; 0、二叉平衡树 左右子树高度差不超过1的二叉搜索树。 public class AVLTree{static class AVLTreeNode {public TreeNode left null; // 节点的左孩子public TreeNode right null; // 节点的…

MySQL之基本查询(上)-表的增删查改

目录 Create(创建) 案例建表 插入 单行数据 指定列插入 单行数据 全列插入 多行数据 全列插入 插入是否更新 插入时更新 替换 Retrieve(读取) 建表插入 select列 全列查询 指定列查询 查询字段为表达式 为查询结果指定别名 结果去重 where条件 比较运算符 逻辑运…

【UE5.1 角色练习】16-枪械射击——瞄准

目录 效果 步骤 一、瞄准时拉近摄像机位置 二、瞄准偏移 三、向指定方向射击 四、连发 效果 步骤 一、瞄准时拉近摄像机位置 打开角色蓝图&#xff0c;在事件图表中添加如下节点&#xff0c;当进入射击状态时设置目标臂长度为300&#xff0c;从而拉近视角。 但是这样切…

数学建模中常用的数据处理方法

常用的数据处理方法 本文参考 B站西电数模协会的讲解视频 &#xff0c;只作笔记提纲&#xff0c;想要详细学习具体内容请观看 up 的学习视频。国赛的 C 题一般数据量比较大。 这里介绍以下两种方法&#xff1a; 数据预处理方法 数据分析方法 数据预处理方法 1. 数据清洗 为…

【后端开发实习】用MongoDB实现仓库管理的出库入库实战

用MongoDB实现仓库管理的出库入库 MongoDB什么是MongoDBMongoDB安装以及开始运行配置启动以及mongoshmongodb的基础使用命令启动和使用MongoDB服务数据库操作集合操作文档操作 项目部署在数据库中创建一张商品信息表提供信息表的增删改查操作接口 MongoDB 什么是MongoDB Mong…

德语中含“Augen”的惯用语表达-柯桥小语种学习德语考级

在我们的德语学习过程中&#xff0c;除了词汇的记忆&#xff0c;另一项重要的记忆任务就是惯用语的背诵啦。要知道&#xff0c;德语中有大量的Redewendung&#xff0c;他们以其言简意赅的表达&#xff0c;在日常用语中备受青睐。上一期我们已经学习了部分含有“Hand”的惯用语&…

每日一题~abc356(对于一串连续数字 找规律)

添加链接描述 题意&#xff1a;对于给定的n,m 。计算0~n 每一个数和m & 之后&#xff0c;得到的数 的二进制中 1的个数的和。 一位一位的算。最多是60位。 我们只需要计算 在 1-n这些数上&#xff0c;有多少个数 第i位 为1. 因为是连续的自然数&#xff0c;每一位上1 的…

ARM学习(29)NXP 双coreMCU IMX1160学习----NorFlash 启动引脚选择

ARM学习&#xff08;28&#xff09;NXP 双coreMCU IMX1160学习----NorFlash 启动引脚选择 1、多种启动方式介绍 IMX1166 支持多组flexSPI 引脚启动&#xff0c;FlexSPI1以及FlexSPI2&#xff0c;通过boot cfg可以切换FlexSPI得实例。 每个实例又支持多组引脚&#xff0c;总共…

Let‘s Encrypt性价比最高的申请SSL证书

SSL/TLS证书作为确保网站数据传输安全性的重要手段&#xff0c;受到了广大网站运营者的青睐。然而&#xff0c;高昂的证书费用往往成为许多小型网站和个人博客的负担。 申请Lets Encrypt免费泛域名SSL证书步骤 1. 登录来此加密网站&#xff0c;输入域名&#xff0c;可以勾选泛…

食物链之带权并查集解法

直接看题&#xff1a;https://www.acwing.com/problem/content/description/242/ 下面就是代码的实现了&#xff0c;因为自己与自己肯定是同类我们初始化为0. 下面是AC代码&#xff1a; #include<bits/stdc.h> using namespace std; int n,k; int fk,x,y; int fa[10001…

染色法判定二分图

什么是二分图&#xff1f; 二分图&#xff0c;也称作二部图&#xff0c;是图论中的一种特殊模型。在一个无向图G(V,E) 中&#xff0c;如果顶点集合 V 可以被分割成两个互不相交的子集 A 和 B&#xff0c;并且图中的每条边 (i,j) 关联的两个顶点 i 和 j 分别属于这两个不同的顶…

第三课网关作用

实验拓扑图&#xff1a; 基础配置&#xff1a; PC1的基础配置 PC2的基础配置&#xff1a; PC4的基础配置 AR1添加PC4网段: 并且添加pc1,pc2的网段。 并且添加pc1,pc2的网段。 原理&#xff1a;PC4先把数据交给100.100.100.1&#xff0c;交给了路由器&#xff0c;路由器再把数…

分贝通差旅管理费控BI 助力企业差旅报销降本

在企业的日常运营中&#xff0c;每张机票、每个酒店预订、每次用车、每笔对公付款&#xff0c;这些看似零碎的支出累积起来&#xff0c;每月往往能轻松达到数百万元。面对如此庞大的支出&#xff0c;如何及时发现并控制不合理支出&#xff0c;成为企业成本控制的关键。为此&…

引用计数器(kref)

1、什么是引用计数器 如果我们写了一个字符驱动&#xff0c;当硬件设备插上时&#xff0c;系统会生成一个设备节点。用户在应用空间操作这个设备节点就可以操作设备。如果此时将硬件断开&#xff0c;驱动是不是就要立刻释放呢&#xff1f;如果立刻释放&#xff0c;应用程序是不…

计算机网络体系结构解析

OSI参考模型 与 TCP/IP模型 如图所示 TCP/IP模型有几层 应用层&#xff1a;只需要专注于为用户提供应用功能 HTTP、SMTP、Telnet等&#xff0c;工作在操作系统中的用户态&#xff0c;传输层及以下工作在内核态传输层&#xff1a;为应用层提供网络支持&#xff08;TCP、UDP传…

《昇思25天学习打卡营第01天|qingyun201003》

打卡 日期 心得 我的主语言并不是Python,以及现在从事的工作也并不是开发&#xff1b;所以对于这个系列的课程&#xff0c;学习起来是较为困难的&#xff0c;所以基于这种情况&#xff0c;该如何进行学习&#xff1f;我的做法是全部交给AI&#xff0c;使用AI一步步解析代码&a…