etcd raft library设计原理和使用

早在2013年11月份,在raft论文还只能在网上下载到草稿版时,我曾经写过一篇blog对其进行简要分析。4年过去了,各种raft协议的讲解铺天盖地,raft也确实得到了广泛的应用。其中最知名的应用莫过于etcd。etcd将raft协议本身实现为一个library,位于https://github.com/coreos/etcd/tree/master/raft,然后本身作为一个应用使用它。

本文不讲解raft协议核心内容,而是站在一个etcd raft library使用者的角度,讲解要用上这个library需要了解的东西。

这个library使用起来相对来说还是有点麻烦。官方有一个使用示例在 https://github.com/coreos/etcd/tree/master/contrib/raftexample。整体来说,这个库实现了raft协议核心的内容,比如append log的逻辑,选主逻辑,snapshot,成员变更等逻辑。需要明确的是:library没有实现消息的网络传输和接收,库只会把一些待发送的消息保存在内存中,用户自定义的网络传输层取出消息并发送出去,并且在网络接收端,需要调一个library的函数,用于将收到的消息传入library,后面会详细说明。同时,library定义了一个Storage接口,需要library的使用者自行实现。

Storage接口如下:

// Storage is an interface that may be implemented by the application
// to retrieve log entries from storage.
//
// If any Storage method returns an error, the raft instance will
// become inoperable and refuse to participate in elections; the
// application is responsible for cleanup and recovery in this case.
type Storage interface {// InitialState returns the saved HardState and ConfState information.InitialState() (pb.HardState, pb.ConfState, error)// Entries returns a slice of log entries in the range [lo,hi).// MaxSize limits the total size of the log entries returned, but// Entries returns at least one entry if any.Entries(lo, hi, maxSize uint64) ([]pb.Entry, error)// Term returns the term of entry i, which must be in the range// [FirstIndex()-1, LastIndex()]. The term of the entry before// FirstIndex is retained for matching purposes even though the// rest of that entry may not be available.Term(i uint64) (uint64, error)// LastIndex returns the index of the last entry in the log.LastIndex() (uint64, error)// FirstIndex returns the index of the first log entry that is// possibly available via Entries (older entries have been incorporated// into the latest Snapshot; if storage only contains the dummy entry the// first log entry is not available).FirstIndex() (uint64, error)// Snapshot returns the most recent snapshot.// If snapshot is temporarily unavailable, it should return ErrSnapshotTemporarilyUnavailable,// so raft state machine could know that Storage needs some time to prepare// snapshot and call Snapshot later.Snapshot() (pb.Snapshot, error)
}

这些接口在library中会被用到。熟悉raft协议的人不难理解。上面提到的官方示例https://github.com/coreos/etcd/tree/master/contrib/raftexample中使用了library自带的MemoryStorage,和etcd的wal和snap包做持久化,重启的时候从wal和snap中获取日志恢复MemoryStorage。

要提供这种IO/网络密集型的东西,提高吞吐最好的手段就是batch加批处理了。etcd raft library正是这么做的。

下面看一下为了做这事,etcd提供的核心抽象Ready结构体:

// Ready encapsulates the entries and messages that are ready to read,
// be saved to stable storage, committed or sent to other peers.
// All fields in Ready are read-only.
type Ready struct {// The current volatile state of a Node.// SoftState will be nil if there is no update.// It is not required to consume or store SoftState.*SoftState// The current state of a Node to be saved to stable storage BEFORE// Messages are sent.// HardState will be equal to empty state if there is no update.pb.HardState// ReadStates can be used for node to serve linearizable read requests locally// when its applied index is greater than the index in ReadState.// Note that the readState will be returned when raft receives msgReadIndex.// The returned is only valid for the request that requested to read.ReadStates []ReadState// Entries specifies entries to be saved to stable storage BEFORE// Messages are sent.Entries []pb.Entry// Snapshot specifies the snapshot to be saved to stable storage.Snapshot pb.Snapshot// CommittedEntries specifies entries to be committed to a// store/state-machine. These have previously been committed to stable// store.CommittedEntries []pb.Entry// Messages specifies outbound messages to be sent AFTER Entries are// committed to stable storage.// If it contains a MsgSnap message, the application MUST report back to raft// when the snapshot has been received or has failed by calling ReportSnapshot.Messages []pb.Message// MustSync indicates whether the HardState and Entries must be synchronously// written to disk or if an asynchronous write is permissible.MustSync bool
}

可以说,这个Ready结构体封装了一批更新,这些更新包括:

  • pb.HardState: 包含当前节点见过的最大的term,以及在这个term给谁投过票,已经当前节点知道的commit index
  • Messages: 需要广播给所有peers的消息
  • CommittedEntries:已经commit了,还没有apply到状态机的日志
  • Snapshot:需要持久化的快照

库的使用者从node结构体提供的一个ready channel中不断的pop出一个个的Ready进行处理,库使用者通过如下方法拿到Ready channel:

func (n *node) Ready() <-chan Ready { return n.readyc }

应用需要对Ready的处理包括:

  1. 将HardState, Entries, Snapshot持久化到storage。
  2. 将Messages(上文提到的msgs)非阻塞的广播给其他peers
  3. 将CommittedEntries(已经commit还没有apply)应用到状态机。
  4. 如果发现CommittedEntries中有成员变更类型的entry,调用node的ApplyConfChange()方法让node知道(这里和raft论文不一样,论文中只要节点收到了成员变更日志就应用)
  5. 调用Node.Advance()告诉raft node,这批状态更新处理完了,状态已经演进了,可以给我下一批Ready让我处理。

应用通过raft.StartNode()来启动raft中的一个副本,函数内部通过启动一个goroutine运行

func (n *node) run(r *raft)

来启动服务。

应用通过调用

func (n *node) Propose(ctx context.Context, data []byte) error

来Propose一个请求给raft,被raft开始处理后返回。

增删节点通过调用

func (n *node) ProposeConfChange(ctx context.Context, cc pb.ConfChange) error

node结构体包含几个重要的channel:

// node is the canonical implementation of the Node interface
type node struct {propc      chan pb.Messagerecvc      chan pb.Messageconfc      chan pb.ConfChangeconfstatec chan pb.ConfStatereadyc     chan Readyadvancec   chan struct{}tickc      chan struct{}done       chan struct{}stop       chan struct{}status     chan chan Statuslogger Logger
}
  • propc: propc是一个没有buffer的channel,应用通过Propose接口写入的请求被封装成Message被push到propc中,node的run方法从propc中pop出Message,append自己的raft log中,并且将Message放入mailbox中(raft结构体中的msgs []pb.Message),这个msgs会被封装在Ready中,被应用从readyc中取出来,然后通过应用自定义的transport发送出去。
  • recvc: 应用自定义的transport在收到Message后需要调用

    func (n *node) Step(ctx context.Context, m pb.Message) error
    来把Message放入recvc中,经过一些处理后,同样,会把需要发送的Message放入到对应peers的mailbox中。后续通过自定义transport发送出去。
  • readyc/advancec: readyc和advancec都是没有buffer的channel,node.run()内部把相关的一些状态更新打包成Ready结构体(其中一种状态就是上面提到的msgs)放入readyc中。应用从readyc中pop出Ready中,对相应的状态进行处理,处理完成后,调用

    rc.node.Advance()
    往advancec中push一个空结构体告诉raft,已经对这批Ready包含的状态进行了相应的处理,node.run()内部从advancec中得到通知后,对内部一些状态进行处理,比如把已经持久化到storage中的entries从内存(对应type unstable struct)中删除等。
  • tickc:应用定期往tickc中push空结构体,node.run()会调用tick()函数,对于leader来说,tick()会给其他peers发心跳,对于follower来说,会检查是否需要发起选主操作。
  • confc/confstatec:应用从Ready中拿出CommittedEntries,检查其如果含有成员变更类型的日志,则需要调用

    func (n *node) ApplyConfChange(cc pb.ConfChange) *pb.ConfState

    这个函数会push ConfChange到confc中,confc同样是个无buffer的channel,node.run()内部会从confc中拿出ConfChange,然后进行真正的增减peers操作,之后将最新的成员组push到confstatec中,而ApplyConfChange函数从confstatec pop出最新的成员组返回给应用。

可以说,要想用上etcd的raft library还是需要了解不少东西的。

转载于:https://www.cnblogs.com/foxmailed/p/7137431.html

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

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

相关文章

halcon通过点拟合圆形,鼠标选点

原图 源码 read_image (Image, 0.bmp) dev_clear_window () dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle) dev_display (Image)binary_threshold (Image, Region, max_separability, dark, UsedThreshold) connection (Region, ConnectedRegions) select_s…

JDBC事务--软件开发三层架构--ThreadLocal

JDBC事务--软件开发三层架构--ThreadLocal 一.JDBC事务 1.概述: 事务是指逻辑上的一组操作!这一组操作,通常认为是一个整体,不可拆分! 特点:同生共死;事务内的这一组操作要么全部成功,要么全部失败! 作用:保证逻辑操作的完整性,安全性! 2.使用(3种方式) 1)面向数据库,使用S…

LINUX多播编程

一.单播&#xff0c;广播和多播 1.单播用于两个主机之间的端对端通信&#xff0c;广播用于一个主机对整个局域网上所有主机上的数据通信。单播和广播是两个极端&#xff0c;要么对一个主机进行通信&#xff0c;要么对整个局域网上的主机进行通信。实际情况下&#xff0c;经常需…

cas单点登录搭建

Cas Server下载&#xff1a;http://developer.jasig.org/cas/ Cas Client下载&#xff1a;http://developer.jasig.org/cas-clients/ 测试环境&#xff1a; jdk&#xff1a;java version "1.8.0_60" tomcat&#xff1a;apache-tomcat-7.0.65 mysql&#xff1a;mysql5…

新CIO:Mark Schwartz认为的领先IT

美国公民及移民服务局前任CIO&#xff0c;现任AWS企业战略师Mark Schwartz在伦敦举行的DevOps企业峰会上介绍了什么是领先的IT。\\Schwartz介绍说&#xff0c;老旧、传统的模型将业务和IT完全分开&#xff0c;他又提出了一种新的模型&#xff0c;在这种模型中&#xff0c;CIO担…

689D Magic Odd Square 奇数幻方

1 奇数阶幻方构造法 (1) 将1放在第一行中间一列; (2) 从2开始直到nn止各数依次按下列规则存放&#xff1a;按 45方向行走&#xff0c;向右上&#xff0c;即每一个数存放的行比前一个数的行数减1&#xff0c;列数加1 (3) 如果行列范围超出矩阵范围&#xff0c;则回绕。例如1在第…

Java单例的常见形式

2019独角兽企业重金招聘Python工程师标准>>> Java单例的常见形式 本文目的&#xff1a;总结Java中的单例模式 本文定位&#xff1a;学习笔记 学习过程记录&#xff0c;加深理解&#xff0c;便于回顾。也希望能给学习的同学一些灵感 一、非延迟加载单例类 public cla…

运动控制卡的基类函数与实现例子

基类 namespace MotionCardDll {public abstract class IMotionCard{public Int32 m_Mode;public Int32 m_BoardId;//Card 号public Int32 m_Card_name;public Int32 m_StartAxisID

U-Boot启动过程完全分析

1.1 U-Boot 工作过程 U-Boot启动内核的过程可以分为两个阶段&#xff0c;两个阶段的功能如下&#xff1a; &#xff08;1&#xff09;第一阶段的功能 硬件设备初始化 加载U-Boot第二阶段代码到RAM空间 设置好栈 跳转到第二阶段代码入口 &#xff08;2&#x…

CJOJ 2171 火车站开饭店(树型动态规划)

CJOJ 2171 火车站开饭店&#xff08;树型动态规划&#xff09; Description 政府邀请了你在火车站开饭店&#xff0c;但不允许同时在两个相连的火车站开。任意两个火车站有且只有一条路径&#xff0c;每个火车站最多有 50 个和它相连接的火车站。 告诉你每个火车站的利润&#…

JavaWeb总结(十五)

AJAX&#xff08;Asynchronous JavaScript and XML&#xff08;异步的 JavaScript 和 XML&#xff09;&#xff09; AJAX的作用是什么&#xff1f; 在无需重新加载整个网页的情况下&#xff0c;能够更新部分网页的技术 是一种用于创建快速动态网页的技术 通过在后台与服务器进行…

工业相机基类与实现

基类 namespace Cameron {//相机参数public struct CamPara{public int DeviceID; //设备描述public string Name;public int WorkMode; //工作类型,0为连续模式,1为触发模式public float Expours

物联网技术周报第 143 期: Unity 3D 和 Arduino 打造虚拟现实飞行器

新闻 \\\\t《西门子、阿里云签约助力中国工业物联网发展》德国工业集团西门子和中国阿里巴巴集团旗下的云计算公司阿里云&#xff19;日在柏林签署备忘录&#xff0c;共同推进中国工业物联网发展。根据备忘录内容&#xff0c;西门子和阿里云将发挥各自技术和行业优势&#xff…

不同平台下 sleep区别用法

应用程序&#xff1a; #include <syswait.h> usleep(n) //n微秒 Sleep&#xff08;n&#xff09;//n毫秒 sleep&#xff08;n&#xff09;//n秒 驱动程序&#xff1a; #include <linux/delay.h> mdelay(n) //微秒milliseconds 其实现 #ifdef notdef #define mdelay…

各视频、各音频之间格式任意玩弄(图文详解)

写在前面说的话 在这里&#xff0c;记录下来&#xff0c;是为了方便以后偶尔所制作所需和你们前来的浏览学习。 学会&#xff0c;玩弄一些视频和音频的软件&#xff0c;只有好处没有害处。同时&#xff0c;也不需很多时间&#xff0c;练练手罢了。也是方便自己所用吧&#xff0…

oracle 如何查看日志?

2019独角兽企业重金招聘Python工程师标准>>> Oracle日志查看一&#xff0e;Oracle日志的路径&#xff1a;登录&#xff1a;sqlplus "/as sysdba"查看路径&#xff1a;SQL> select * from v$logfile;SQL> select * from v$logfile;(#日志文件路径)二…

回归_英国酒精和香烟关系

sklearn实战-乳腺癌细胞数据挖掘(博客主亲自录制视频教程) https://study.163.com/course/introduction.htm?courseId1005269003&utm_campaigncommission&utm_sourcecp-400000000398149&utm_mediumshare 数据统计分析联系:&#xff31;&#xff31;&#xff1a;&a…

C# ini文件读写函数

namespace Tools {class IniOperate{[DllImport("kernel32")]private static extern int GetPrivateProfileString(string section, string key,

Visual studio内存泄露检查工具--BoundsChecker

BoundsChecker是一个Run-Time错误检测工具&#xff0c;它主要定位程序在运行时期发生的各种错误。 BoundsChecker能检测的错误包括&#xff1a; 1&#xff09;指针操作和内存、资源泄露错误&#xff0c;比如&#xff1a;内存泄露&#xff1b;资源泄露&#xff…

【转】如何用Maven创建web项目(具体步骤)

使用eclipse插件创建一个web project 首先创建一个Maven的Project如下图 我们勾选上Create a simple project &#xff08;不使用骨架&#xff09; 这里的Packing 选择 war的形式 由于packing是war包&#xff0c;那么下面也就多出了webapp的目录 由于我们的项目要使用eclipse发…