存储和节点的创建

raftexample中的存储其实有两种,一个是通过raft.NewMemoryStorage()进行创建的raft.raftStorage,关联到单个raft节点,另一个是通过newKVStore创建的kv存储,用于服务来自外部的访问。

节点启动时raft.raftStorage的加载

上一篇中主要围绕replayWAL介绍wal的读写,到本文为止可以完整拼接出该函数的处理逻辑。其中snapshot的作用是通过index限定了加载的wal日志的范围。

  1. 一开始会通过loadSnapshot函数找出一个有效且最新的snapshot。保证有效的方式是:从raftNode.waldir中读取所有snapshotType类型的数据(snaps),以及最新的stateType类型的数据(state),然后过滤出所有index<=state.Commit的snapshot(state.Commit表示已提交的日志的index,从本地加载的snapshot的index不能超过state.Commit,否则被认为是无效的历史数据);保证最新的方式是:通过对比raftNode.snapdir(注意对比时文件是倒序的)和上一步获取到的snaps(即通过对比Snapshot.TermSnapshot.Index),找出最新的snapshot。

  2. openWAL函数根据第1步中获取到的snapshot的index找出符合条件(即index<=snapshot.index)的wal文件

  3. ReadAll()会从上一步的wal文件中读取index大于snapshot.index的entryType类型的表项(wal的start就是snapshot),以及stateType类型的状态信息。

    case entryType:
    e := mustUnmarshalEntry(rec.Data)
    // 0 <= e.Index-w.start.Index - 1 < len(ents)
    if e.Index > w.start.Index {
    // prevent "panic: runtime error: slice bounds out of range [:13038096702221461992] with capacity 0"
    up := e.Index - w.start.Index - 1
    if up > uint64(len(ents)) {
    // return error before append call causes runtime panic
    return nil, state, nil, ErrSliceOutOfRange
    }
    // The line below is potentially overriding some 'uncommitted' entries.
    ents = append(ents[:up], e)
    }
    w.enti = e.Index

总结一下,上面的关系如下,state限定了读取的snapshot的范围(起点),而snapshot限定了读取的ents的范围。

三者的大小关系如下:snapshot.Index<=state.Commit,ents>snapshot.Index。

后续就是将snapshot、state和ents保存到raftStorage中(raftexample的存储实际并不支持持久化,下面以"落库"表示将数据保存到存储中),需要注意的是,落库的内容也受snapshot的限制,即只保存index>snapshot.index的表项(ApplySnapshot函数会使用snapshot的覆盖原有数据,由此可见raftStorage中并没有保存完整的数据)。从上图可以看出所有处理逻辑都以state为起点进行的,state表示raft的处理结果,可以直接落库。当保存snapshot时,需要与存储中的snapshot进行比较,只有当index大于存储中的snapshot的index时才会被保存。在保存ents时,由于ReadAll接口(见上)对读取的ents的范围作了限制,因此只可能出现以下3种情况:1)ents的数据的最大index小于存储中的最小index,这种情况不做任何处理;2)ents的数据的最小index等于存储中的最小index,这种情况直接追加到存储即可;3)ents的数据和存储的数据有交叉,这种情况需要剔除重叠的数据,并追加新的数据。

snapshot中除了保存了索引相关的内容,还保存了与集群状态有关的信息(snapshot.Metadata.ConfState),用于恢复集群状态。

kvStore的存储

节点运行中主要通过raftNode.commitC来通知kvStore。一种是在接收到snapshot时先保存snapshot,然后通过给rc.commitC传递nil来触发kvStore从snap目录中加载snapshot;另一种就是直接通过rc.commitC将接收到的entries保存到kvStore。

raftLog

raft通过raftLog与raftStorage存储进行交互,unstable可以看作是storage的缓存,保存着未进入raftStorage的数据(entires和snapshot)。当需要获取raftLog的首末索引时,会优先从unstable中查找,若找不到,再从raftStorage中查找。

raftNode的创建

下图展示了raft节点的创建过程,从下图可以看到,raft节点启动时主要涉及的是raftLogProgressTrackermsgs。上一节已经讲过raftlog,它作为raft的存储,包括unstable和storage。ProgressTracker是raft中用来跟踪集群(config)以及各个节点的状态(prs)。raft集群中的节点分为Voters,Learners和LearnersNext三大类,其中前两个是互斥的,即一个raft节点只能属于其中一类。最后一类存在的原因是为了在voters(outgoing)向learner转换过程中保证一致性(受raft的joint行为限制,raft的状态变更是通过joint方式运作的),是个临时状态。Voters又分为incoming(Voters[0])和outgoing(Voters[1])两种,分别表示新状态和原始状态。当发生状态变更时,会将当前状态拷贝到outgoing中,根据新状态类型(ConfChangeAddNode/ConfChangeAddLearnerNode/ConfChangeRemoveNode/ConfChangeUpdateNode)来执行joint操作,如果raft处于joint状态(outgoing长度大于0),则不能再次执行joint(有可能存在中间状态)。

raftLog中有两个标识:committed和applied,committed标识已经进入storage中的最大日志位置,而applied表示已经处理过的消息(如配置变更等)。其中applied <= committed。

ProgressTracker中保存的节点状态有如下三种:

  • StateProbe:每个心跳周期只能发送一个复制消息,同时用来探测follower的进度。
  • StateReplicate:为follower可以快速接收赋值日志的理想状态
  • StateSnapshot:在发送赋值消息前需要发送snapshot,来让follower转变为StateReplicate状态

原图地址

raft的joint操作以及LearnersNext存在的原因见下:

	// When we turn a voter into a learner during a joint consensus transition,
// we cannot add the learner directly when entering the joint state. This is
// because this would violate the invariant that the intersection of
// voters and learners is empty. For example, assume a Voter is removed and
// immediately re-added as a learner (or in other words, it is demoted):
//
// Initially, the configuration will be
//
// voters: {1 2 3}
// learners: {}
//
// and we want to demote 3. Entering the joint configuration, we naively get
//
// voters: {1 2} & {1 2 3}
// learners: {3}
//
// but this violates the invariant (3 is both voter and learner). Instead,
// we get
//
// voters: {1 2} & {1 2 3}
// learners: {}
// next_learners: {3}
//
// Where 3 is now still purely a voter, but we are remembering the intention
// to make it a learner upon transitioning into the final configuration:
//
// voters: {1 2}
// learners: {3}
// next_learners: {}
//
// Note that next_learners is not used while adding a learner that is not
// also a voter in the joint config. In this case, the learner is added
// right away when entering the joint configuration, so that it is caught up
// as soon as possible.

上图中的涉及新raft节点的创建,整个过程也比较简单。首先根据replayWAL获取到的snapshot来恢复集群和节点的状态,然后根据本节点是否是leader来执行更新raftLog的提交记录和消息发送。

raftNode的运行

下图给出了raftNode的基本运作流程,仅含初始的选举流量,但后续处理方式也大体类似,区别在于传递和处理的消息类型不同。在raftExample的讲解中可以看到启动了一个serveChannels的服务,该服务用于从n.readyc中接收封装好的ready消息,然后进行保存、应用(配置)和发送等操作,最后通过n.advancec通知node节点处理结果,node以此执行acceptReady操作,更新提交记录和应用记录等。

serveChannels中会创建一个100ms的时钟,定时向n.tickc发送触发消息。一开始,所有节点角色都是follower,此时会触发tickElection(follower和candidate为tickElection,leader为tickHeartbeat),尝试将角色转变为candidate。此外ProgressTracker.Votes中保存了支持本节点作为leader的票数,超过一半节点同意时会转变为leader。

n.readyc中传递的ready消息封装了本节点的状态变更以及待发送的信息,n.readyc中的消息进而会传递给serveChannels,后续通过transport.send发送给其他节点,反之亦然。

TIPs

  • etcd的raft角色有三种:leader、follower、learner。在etcd 3.4之前出现可能会出现如下问题:

    • 新加入一个节点,leader会将快照同步到该节点,但如果快照数量过大,可能会导致超时,导致节点加入失败
    • 新加一个节点时,如果新的节点配置错误(如url错误),可能会导致raft选举失败,集群不可用

    为了避免如上问题,加入了一个新的角色learner,它作为一个单独的节点,在日志同步完成之前不参与选举,etcd中需要通过member promote命令来让learner参与选举。

参考

存储模块源码简析

etcd的raft实现之tracker&quorum

etcd raft 处理流程图系列3-wal的存储和运行的更多相关文章

  1. etcd raft 处理流程图系列1-raftexample

    最近在看raft相关的代码和实现,发现etcd的raft模块在实现上还是比较灵活的,但缺点就是需要用户实现比较多的功能,如存储和网络等,同时带来的优点就是不会对用户的存储和传输作限制.网上对该模块的描 ...

  2. etcd raft 处理流程图系列2-wal的读写

    本文仅介绍wal的基本处理,如create.open.close.read等操作.鉴于篇幅原因,下面介绍replayWAL(启动raft节点时执行)函数涉及的读文件操作:从wal目录中加载snapsh ...

  3. etcd raft 处理流程图系列2-transport

    本章给出了raftexample中使用的传输层代码,补全了上一节中传输层与raft节点(raft server和channel server)的交互细节.下图中流程的核心在于传输层中的streamRt ...

  4. etcd raft library设计原理和使用

    早在2013年11月份,在raft论文还只能在网上下载到草稿版时,我曾经写过一篇blog对其进行简要分析.4年过去了,各种raft协议的讲解铺天盖地,raft也确实得到了广泛的应用.其中最知名的应用莫 ...

  5. 彻底搞懂etcd raft选举、数据同步

    etcd raft选举机制 etcd 是一个分布式的k/V存储系统.核心使用了RAFT分布式一致性协议.一致性这个概念,它是指多个服务器在状态达成一致,但是在一个分布式系统中,因为各种意外可能,有的服 ...

  6. etcd raft如何实现成员变更

    成员变更在一致性协议里稍复杂一些,由于不同的成员不可能在同一时刻从旧成员组切换至新成员组,所以可能出现两个不相交的majority,从而导致同一个term出现两个leader,进而导致同一个index ...

  7. etcd raft library

    https://github.com/coreos/etcd/tree/master/raft import "github.com/coreos/etcd/raft" ----- ...

  8. [转帖]TPC-C解析系列05_TPC-C基准测试之存储优化

    TPC-C解析系列05_TPC-C基准测试之存储优化 http://www.itpub.net/2019/10/08/3332/ 蚂蚁金服科技 2019-10-08 11:27:02 本文共3664个 ...

  9. etcd raft如何实现leadership transfer

    leadership transfer可以把raft group中的leader身份转给其中一个follower.这个功能可以用来做负载均衡,比如可以把leader放在性能更好的机器或者离客户端更近的 ...

随机推荐

  1. EasyUI:combotree(树形下拉框)复选框选中父节点(子节点的状态也全部选中)输入框中只显示父节点的文本值

    参考: https://blog.csdn.net/weixin_43236850/article/details/100320564

  2. Python_结合Re正则模块爬虫

    ##### 爬取古诗文import reimport requestsdef parse_page(url): headers = { 'User-Agent':'Mozilla/5.0 (Windo ...

  3. Jmeter任在运行,线程数上不去

    问题 jmeter在运行,但是线程数上不去(本来模型设计了100个总线程,但运行时线程只能上到5,根据图上观察总共也只能运行5个线程) 之前更新了random csv插件 解决办法 查看jmeter. ...

  4. javascript学习笔记-(三)

    ES6标准新增了一种新的函数:Arrow Function(箭头函数) 案例: 为什么叫Arrow Function?因为它的定义用的就是一个箭头: x => x * x 上面的箭头函数相当于: ...

  5. Ubuntu21.04 / Linux Mint20.2 安装 TradingView分析软件

    TradingView 是我们经常使用的一个分析工具!在Linux 下安装桌面版,就可以爽快的用起来了,目前是Beta测试版,相信很快就会有正式版的 1.打开终端,执行下列命令,更新系统到最新 sud ...

  6. spring第三章

    第三章 实现AOP AOP:面向方面编程,AOP能够使您将所有模块共有的特性与应用程序的主要业务逻辑隔离开 一.AOP介绍 横切关注点:在Web应用程序中,有一些服务(如登录.安全和事务管理)不是应用 ...

  7. vulnhub-DC:4靶机渗透记录

    准备工作 在vulnhub官网下载DC:4靶机https://www.vulnhub.com/entry/dc-4,313/ 导入到vmware,设置成NAT模式 打开kali准备进行渗透(ip:19 ...

  8. 基于MD5+RSA算法实现接口调用防扯皮级鉴权

    概述 最近项目中需要对第三方开发接口调用,考虑了一下,准备采用MD5+RSA算对请求数据进行签名,来达到请求鉴权,过滤非法请求的目标. 数字签名采用MD5+RSA算法实现.RSA私钥要严格保密并提供安 ...

  9. 比@EnableMongoAuditing功能强大的实现

    问题出现 以前通过@EnableMongoAuditing.@CreateDate.@LastModifiedDate进行实体类创建时间.修改时间的自动管理. 但为了实现多数据源的管理以及切换,自己覆 ...

  10. 离线webpack创建vue 项目

    参考地址: https://blog.csdn.net/feinifi/article/details/104578546 画重点: // 需要带上参数--offline表示离线初始化. --offl ...