分布式程序的协调服务-ZooKeeper

目录

  1. 安装配置
  2. Paxos简介
  3. ZooKeeper的基本原理
  4. ZooKeeper的基本概念
  5. ZooKeeper的客户端API
  6. ZooKeeper的应用
  7. 附录:

ZooKeeper的安装配置

准备工作:

  • 安装JAVA(略过)
  • 点击这里下载ZooKeeper的安装包(略过)

接下来,安装一个ZooKeeper 3.4.9伪集群:

  • 解压:
tar zxvf zookeeper-3.4.9.tar.gz  
  • 复制ZooKeeper目录:
for i in `seq 1 4`; do cp -r zookeeper-3.4.9 zookeeper-3.4.9-$i; done  
  • 创建zoo.cfg
for i in `seq 1 4`; do cp zookeeper-3.4.9-$i/conf/zoo_sample.cfg zookeeper-3.4.9-$i/conf/zoo.cfg ; done  
  • zoo.cfg的常用参数(详解请点击这里):

    • tickTime:ZK中的一个时间单元。ZK中所有时间都是以这个时间单元为基础,进行整数倍配置的。例如,session的最小超时时间是2*tickTime
    • initLimit:Follower启动时,会从Leader同步状态,同步完成后,才被Leader加入到Follower列表。Leader允许F在 initLimit 时间内完成这个工作。通常情况下,我们不用太在意这个参数的设置。如果ZK集群的数据量确实很大了,F在启动的时候,从Leader上同步数据的时间也会相应变长,因此在这种情况下,有必要适当调大这个参数
    • syncLimit:在运行过程中,Leader负责与ZK集群中所有机器进行通信,例如通过一些心跳检测机制,来检测机器的存活状态。如果L发出心跳包在syncLimit之后,还没有从F那里收到响应,那么就认为这个F已经不在线了。注意:不要把这个参数设置得过大,否则可能会掩盖一些问题
    • dataDir:存储快照文件snapshot的目录。默认情况下,事务日志也会存储在这里。建议同时配置参数dataLogDir, 事务日志的写性能直接影响zk性能
    • dataLogDir:事务日志输出目录。尽量给事务日志的输出配置单独的磁盘或是挂载点,这将极大的提升ZK性能
    • clientPort:客户端连接server的端口,即对外服务端口,一般设置为2181
    • server.x=[hostname]:nnnnn[:nnnnn]:这里的x是一个数字,与myid文件中的id是一致的。右边可以配置两个端口,第一个端口用于F和L之间的数据同步和其它通信,第二个端口用于Leader选举过程中投票通信
  • 为每个节点创建数据目录、事务日志目录、myid文件(每个节点的myid都应该不一样)

for i in `seq 1 4` ; do mkdir zkdata-$i; mkdir zkdatalog-$i; echo $i >zkdata-$i/myid; done  
  • 修改zoo.cfg的配置:
for i in `seq 1 4`; do sed -i "s/^clientPort=[0-9]\{1,\}/clientPort=218$i/g" zookeeper-3.4.9-$i/conf/zoo.cfg; sed -i "s/^dataDir=.*/dataDir=zkdata-$i\//g" zookeeper-3.4.9-$i/conf/zoo.cfg ; done

for i in `seq 1 4` ; do echo "dataLogDir=zkdatalog-$i/" >>zookeeper-3.4.9-$i/conf/zoo.cfg; for i_ in `seq 1 4`; do echo "server.$i_=0.0.0.0:188$i_:288$i_" >>zookeeper-3.4.9-$i/conf/zoo.cfg; done ; done  
  • 将第四个节点设置为observer
for i in `seq 1 4` ; do sed -i "s/^server.4=\(.*\):\([0-9]\{1,\}\):\([0-9]\{1,\}\)$/server.4=\1:\2:\3:observer/g" zookeeper-3.4.9-$i/conf/zoo.cfg ; done  
  • 启动这四个节点:
for i in `seq 1 4`; do zookeeper-3.4.9-$i/bin/zkServer.sh start ; done  
  • 启动之后,查看每个节点的状态:
$ for i in `seq 1 4`
> do
> zookeeper-3.4.9-$i/bin/zkServer.sh status
> done
ZooKeeper JMX enabled by default  
Using config: C:\cygwin64\home\zhoujingjiang\zookeeper-3.4.9-1\conf\zoo.cfg  
Mode: follower  
ZooKeeper JMX enabled by default  
Using config: C:\cygwin64\home\zhoujingjiang\zookeeper-3.4.9-2\conf\zoo.cfg  
Mode: leader  
ZooKeeper JMX enabled by default  
Using config: C:\cygwin64\home\zhoujingjiang\zookeeper-3.4.9-3\conf\zoo.cfg  
Mode: follower  
ZooKeeper JMX enabled by default  
Using config: C:\cygwin64\home\zhoujingjiang\zookeeper-3.4.9-4\conf\zoo.cfg  
Mode: observer  

Paxos简介

ZooKeeper在恢复模式下,选举leader使用的就是Paxos算法,因此先简单介绍Paxos算法。

分布式一致性算法解决的问题是:分布式系统如何就一个值达成一致。在Paxos算法中,多个节点之间同一个值达成一致的过程叫Paxos instance。
在Basic Paxos算法中,主要有两类角色:

  • Proposer:
    • 负责发起提案,一个提案是由提案编号和value两部分组成的
  • Acceptor:
    • 负责批准提案
    • 在Acceptor内部会维护三个值:
      • minProposal:Acceptor不接受任何编号小于minProposal的提案
      • AcceptedProposal:Acceptor 当前 所接受的最大的提案的编号
      • AcceptedValue:Acceptor 当前 所接受的最大的提案的值

1.jpg

图1-Basic Paxos

如上图所示,Paxos算法分为两个阶段:

  • Prepare阶段
    • 选择一个提案编号n(提案编号是递增、且不重复的),然后向大多数Acceptor发送Prepare(n)
    • 当Acceptor收到Prepare(n)请求时:
      • 如果n 大于 minProposal,表明该提案是一个更(gèng)新的提案,此时,Acceptor将minProposal设置为n,并返回(AcceptedProposal, AcceptedValue)或(null,null)
      • 否则,不回复 或 回复error(也就是说Acceptor只处理提案编号大于minProposal的Prepare请求
  • Accept阶段
    • 等待一段时间,如果Proposer收到了大多数的Acceptor的响应,会向大多数的Acceptor发送Accept(N, V)请求,其中N的值就是n,V是所有的Acceptor返回的Prepare(n)响应中最大的AcceptedProposal所对应的AcceptedValue,如果所有的Acceptor都没返回(AcceptedProposal, AcceptedValue),那么V由Proposer自己指定
      • Acceptor在收到Accept(N, V)请求时:
        • 如果N大于等于minProposal,则Acceptor将minProposal和AcceptedProposal设置为N,将AcceptedValue设置为V,并回复接受
        • 否则,不回复 或 回复error(也就是说Acceptor只接受编号不小于minProposal的Accept请求
      • 等待一段时间,如果Proposer收到了大多数的Acceptor的响应,则达成一致;否则跳转到Prepare阶段的第一步
    • 否则,跳转到Prepare阶段的第一步

上述流程可知,并发情况下,Paxos存在着活锁问题:
1.png

图2-活锁
  1. S1作为提议者,发起prepare(3.1),并在S1,S2和S3达成多数派;
  2. 随后S5作为提议者,发起了prepare(3.5),并在S3,S4和S5达成多数派;
  3. S1发起accept(3.1, value1),由于S3上提议 3.5>3.1,导致accept请求无法达成多数派,S1尝试重新生成提议;
  4. S1发起prepare(4.1),并在S1,S2和S3达成多数派;
  5. S5发起accpet(3.5, value5),由于S3上提议4.1>3.5,导致accept请求无法达成多数派,S5尝试重新生成提议;
  6. S5发起prepare(5.5),并在S3,S4和S5达成多数派,导致后续的S1发起的accept(4.1, value1)失败;
    ......

Paxos算法的一个特点是:每个Proposer都可以提出一个value,但是Proposer并不会“坚持”自己的value,而是通过Prepare阶段,去“学习”其他的Proposer提出的value(图1中的第4步),最终才能达成一致。


ZooKeeper的基本原理

ZooKeeper中的角色主要有以下三类:

角色描述
leader负责发起提议,更新系统状态
follower接受客户端请求,并向客户端返回结果(在收到写请求时会forward给leader)。选举过程中参与投票
observer与follower类似,但是在选举过程中不参与投票,只同步leader的状态。observer的存在是为了扩展系统,提高读取速度
client请求的发起者

系统模型如下所示: 1.jpg

图3-ZK系统模型



ZooKeeper的特性:

  • 最终一致性:
    • ZooKeeper提供的一致性是弱一致性,首先数据的复制有如下规则:ZooKeeper确保对ZNode树的每一个修改都会被复制到集合体中超过半数的机器上。那么就有可能有节点的数据不是最新的而被客户端访问到。并且会有一个时间点,集群的状态是不一致的。也就是说ZooKeeper只保证最终一致性,实时的一致性可以由客户端自己通过调用sync()方法来保证
  • 原子性:
    • 更新只能成功或失败,没有中间状态
  • 顺序性
    • 全局有序:如果在ZK集群的一个节点上,事务a在事务b前面,那么在集群的所有节点上,事务a都在事务b前面
    • 偏序:对于一个客户端,如果事务a在事务b前面发布,那么事务a必将排在事务b前面

ZooKeeper的基本概念

  • 数据模型
    ZooKeeper的数据模型和Unix文件系统很像,都是具有层级关系的树形结构。ZooKeeper中的每个节点叫做ZNode,ZNode可以用其路径唯一标识(注意:ZNode只能使用绝对路径表示,不能使用相对路径,且除根节点外,路径名不能以/结尾)。每个ZNode可以存储少量数据(默认是1M,且可配置注意:因为ZK在运行时会把ZNode加载进内存,所以不建议在ZNode上存储大量数据。)。另外,ZNode上还存储了ACL信息,ZNode的ACL和Unix文件系统的ACL完全不同,每个ZNode的ACL是互相独立的,子节点不会继承父节点的ACL,关于ZK的ACK,可以参考这篇文章

1.jpg

图4-ZK的数据模型


  • ZNode的类型:
    • 持久化节点:创建之后,需要显式的删除
    • 临时节点:创建之后,可以显式的删除;同时,在session结束后,ZK会自动清理该session所创建的所有的临时节点。临时节点不能有子节点
    • 顺序节点:创建顺序节点时,ZK会自动在节点名称后面追加一个递增的序号

下面看一下ZNode的属性:

[zk: localhost:2181(CONNECTED) 19] get /

cZxid = 0x0  
ctime = Thu Jan 01 00:00:00 UTC 1970  
mZxid = 0x0  
mtime = Thu Jan 01 00:00:00 UTC 1970  
pZxid = 0xb0000000d  
cversion = 11  
dataVersion = 0  
aclVersion = 0  
ephemeralOwner = 0x0  
dataLength = 0  
numChildren = 3  
  • 版本号:
    • dataVersion:数据版本号,每次更新节点的数据,dataVersion都会增加1
    • aclVersion:ACL的版本号
    • cversion:子节点的版本号,当ZNode的子节点发生变化时,cversion增加1

下面以数据版本号为例,说明版本号的作用。假设实现一个AtomicInteger,该数据类型提供了原子的incr <number>操作。那么ZK client的做操可能是这样的:

  • get /AtomicInteger 得到数值value1 和 版本号version1
  • set /AtomicInteger value2 version1,其中value2 = value1 + number
    • 如果失败,则证明已经有其它客户端更新数据了,此时需要客户端重试
    • 如果成功,则表示成功的完成了incr <number>操作,并且会导致数据版本号增加1

如果在set时,不校验版本号,那么就可能导致其它的客户端的更新丢失。

  • 事务ID

    • cZxid:创建ZNode的事务的ID
    • mZxid:更新ZNode的数据的事务的ID
  • 时间戳

    • ctime:ZNode的创建时间
    • mtime:ZNode的修改时间

接下来看,ZooKeeper的watch特性
ZooKeeper支持watch操作,client可以在ZNode上设置一个watcher(比如:get /a/znode watch),当该ZNode上发生相应的变化时,就会触发watcher,把发生的事件通知给设置watcher的client。需要注意的是:watcher是一次性的,也就是说触发一次之后,就会被取消,如果想要继续watch,需要client重新设置watcher。


ZooKeeper的客户端API

使用命令行客户端,连接到ZK:

/path/to/zookeeper_install_directory/bin/zkCli.sh -server your_host:your_port  


输入help,可以查看ZK支持的命令:

[zk: 192.168.30.3:2181(CONNECTED) 0] help
ZooKeeper -server host:port cmd args  
        stat path [watch]
        set path data [version]
        ls path [watch]
        delquota [-n|-b] path
        ls2 path [watch]
        setAcl path acl
        setquota -n|-b val path
        history
        redo cmdno
        printwatches on|off
        delete path [version]
        sync path
        listquota path
        rmr path
        get path [watch]
        create [-s] [-e] path data acl
        addauth scheme auth
        quit
        getAcl path
        close
        connect host:port
  • ls /a/znode
    • 列出ZNode的子节点
  • ls /a/znode watch
    • 列出ZNode的子节点,同时在该ZNode上设置一个watcher,当给该ZNode创建子节点或者删除该ZNode的子节点时(也就是发生NodeChildrenChanged事件时),会触发watcher
  • get /a/znode
    • 获取ZNode上保存的数据 和 ZNode的属性
  • get /a/znode watch
    • 获取ZNode上的数据 和 ZNode的属性。同时在该ZNode上设置一个watcher,当set该ZNode时(也就是发生NodeDataChanged事件时),会触发该watcher
  • set /a/znode znode_data
    • 把该ZNode上的数据设置为 znode_data
  • set /a/znode znode_data version
    • 当ZNode的dataVersion等于version时,将该ZNode的数据设置为 znode_data,并且会导致dataVersion增加1;否则,失败
  • create -e /a/znode zode_data
    • 创建一个临时节点/a/znode,并将其数据设置为 znode_data
  • create -s /a/znode znode_data
    • 创建一个顺序节点,ZK会自动在节点名称后追加一个自增的编号,并将这个节点的数据设置为 znode_data
  • create /a/znode znode_data
    • 创建一个持久节点,并将其数据设置为znode_data
  • sync /a/znode
    • 使客户端所连接到的ZK实例,与Leader进行状态同步。sync是异步的,客户端无需等待sync调用的返回

ZooKeeper的应用

  • 1,配置管理
    • 解决多个Job实例共享及更新配置的问题
    • 实现方法:
      • 将配置内容放到ZK的某个ZNode上,比如:/service/common-configuration
      • 所有的Job实例,启动时,都从该ZNode上获取配置,并且在该ZNode上设置一个watcher
      • 当集群管理员修改了该ZNode的数据时,ZK会通知所有的Job,Job需要重新从该ZNode上获取配置,然后重新设置一个watcher
      • 同时,必须处理连接丢失的问题,当发生重连时,需要重新设置watcher
    • demo:
  • 2,Leader选举
    • Leader选举其实就是:多个进程抢一个互斥锁,抢到锁的就成为active进程,没有抢到锁的进程就成为standby进程
    • 实现方法:
      1. 每个进程在启动的时候,都去ZK集群上注册一个临时的顺序节点
      2. 获取其父节点的所有子节点列表
      3. 判断自己是不是序号最小的子节点
        • 如果是,则将isLeader标记设为true
        • 如果不是,找到比自己次小的节点,然后在这个节点上设置一个watcher
      4. 主线程判断isLeader标记,如为true,则表示该进程成为了leader,执行一次real logic;否则,主线程进入阻塞状态,等待唤醒,当主线程被唤醒时,重新判断isLeader标记,如此反复循环......
      5. 当创建次小节点的进程退出时,次小节点就会被ZK删除,并且触发该进程在其上设置的watcher,此时,事件线程需要获取临时节点的父节点的所有子节点,并判断该临时节点是不是序号最小的子节点
        • 如果是,将isLeader标记设置为true,并notify主线程
        • 否则,找到次小节点,并在其上设置一个watcher
      6. 必须考虑连接丢失的情况,当连接挂起或丢失的时候,需要将isLeader设置为false,当重连的时候,需要删除旧的临时顺序节点,创建新的临时顺序节点,并且重新判断,新建的临时顺序节点是不是最小的节点......
    • demo:
  • 3,命名服务
    • 在分布式系统中,通过使用命名服务,客户端能够通过指定的名字来自动发现资源或服务的地址、提供方等信息
    • 实现方法:
      • 服务提供方启动时,在ZK上创建一个临时节点(比如:/service/test_service/ip:port,接下来都以这个ZNode为例),这个操作完成了服务的发布
      • 服务消费方在启动时,获取/service/test_service下的所有的子节点,解析之后,保存到本地cache中,并且watch该路径,当该路径的子节点发生变化时,需要重新获取子节点列表、解析、更新本地cache、设置watcher
        • 当然,服务消费方也可以通过在ZK的某个路径下创建临时节点的方式,来让系统的其它组件知道目前服务的所有消费方的信息
      • 同时,必须考虑连接丢失的情况,当重新连接的时候,服务提供方需要删除旧的临时节点,创建一个新的同名临时节点;服务消费方需要重新设置watcher
    • demo:

接下来的应用场景的实例直接使用curator自带的实现,curator是目前最流行的ZooKeeper Java客户端上面的demo也建议使用curator自带的实现,博主之所以自己实现,是为了说明其原理

  • 4,屏障(Barrier)
    • 屏障会阻塞所有正在该屏障上等待的节点,一直到屏障被删除,这些节点才能继续运行
    • curator的DistributedBarrier类实现了屏障功能
      • 构造方法的参数:
        • client:client
        • barrierPath:用于设置屏障的ZK路径
      • 主要方法:
        • setBarrier:设置屏障
        • removeBarrier:移除屏障
        • waitOnBarrier:等待,一直到屏障被移除
      • 异常处理:
        • DistributeBarrier会监控连接的状态,当连接断开的时候,waitOnBarrier会抛出异常
    • demo:
  • 5,双屏障
    • 当足够多的节点进入到双屏障时,节点才能继续运行;同时,当足够多的节点离开双屏障时,节点才能离开双屏障
    • curator的DistributedDoubleBarrier类实现了双屏障:
      • 构造方法的参数:
        • client:client
        • barrierPath:用来设置双屏障的ZK路径
        • memberQTY:双屏障中成员的数量
      • 主要方法:
        • enter:调用enter方法时,节点会被阻塞,直到所有成员进入到双屏障
        • leave:调用leave方法时,节点会被阻塞,直到所有成员离开了双屏障
    • demo:
  • 6,组员管理
    • 在典型的Master-Slave结构的分布式系统中,Master需要作为“总管”来管理所有的Slave,当有Slave加入,或者有Slave宕机,Master都需要感知到这个事情,然后作出对应的调整,以便不影响整个集群对外提供服务
    • 实现方法:
      • Master在ZooKeeper上创建/service/slaves结点,并设置对该结点的Watcher
      • 每个Slave在启动成功后,创建唯一标识自己的临时性(Ephemeral)结点/service/slaves/${slave_id},并将自己地址(ip/port)等相关信息写入该结点
      • Master收到有新子结点加入的通知后,做相应的处理
      • 如果有Slave宕机,由于它所对应的结点是临时性结点,在它的Session超时后,ZooKeeper会自动删除该结点
      • Master收到有子结点消失的通知,做相应的处理
  • 7,分布式锁
    • curator的实现可以点击这里
    • 下面是博主实现的 读写锁互斥锁的实现就是LeaderElection的实现
      • 读锁是共享的,写锁是独占的;当读锁存在时,其他进程的读锁可立即获取到,但是写锁需要等待;当写锁存在时,其他进程的读锁和写锁都需要等待
      • 实现方法:
        1. 创建一个临时顺序节点,比如/lock/test/read-write-lock-
        2. 如果该节点是序号最小的节点,那么获取锁成功
        3. 否则,如果是写锁,则watch次小的节点,然后线程进入到等待状态;如果是读锁,则watch前一个写锁对应的节点,如果前面不存在写锁,则该读锁获取成功,否则线程进入到等待状态
        4. 当有NodeDeleted事件发生时,唤醒线程,然后跳转到第二步
        5. 当连接挂起或断开时,唤醒线程,使之回到第2步,此时可能会抛出连接相关的异常
        6. 当连接重连的时候,删除旧的ZNode,创建一个新的,然后跳转到第二步
      • demo:

两阶段提交 和 Zab协议

两阶段提交用来保证分布式事务的原子性,两阶段提交中的角色分为:协调者(1个)和参与者(多个),其过程如下:

  1. 协调者记录Prepare T日志,并向所有的参与者发送Prepare T消息
    • 参与者收到Prepare T消息之后,在自身执行事务的预处理,此时存在两种情况:
      • 如果参与者可以提交事务,那么记录日志、并返回Ready T
      • 如果参与者不能提交事务,那么撤销自身的修改、记录日志、返回Not commit T
  2. 协调者收集所有参与者返回的意见,此时存在三种情况:
    • 如有参与者返回Not commit T,那么协调者记录Abort T日志,并给所有参与者发送Abort T消息, 参与者在收到Abort T消息之后,会撤销自身上执行的预操作,记录Abort T日志,并终止事务的提交
    • 如果所有参与者都返回Ready T,那么协调者记录Commit T日志,并给所有参与者发送Commit T消息, 参与者在收到Commit T之后,会记录Commit T日志,并提交事务
    • 如果协调者迟迟没有收到某个参与者的响应,那么认为该参与者返回了VOTE_ABORT T消息, 此时,协调者记录VOTE_ABORT T日志,并向所有的参与者发送VOTE_ABORT T消息, 参与者在收到VOTE_ABORT T消息之后,会撤销自身上执行的预操作,并记录VOTE_ABORT T日志,终止事务的提交

ZooKeeper Atomic Broadcast(简称ZAB,zookeeper原子消息广播协议)是ZooKeeper保证数据一致性的核心算法。它分为两种模式:

  • 恢复模式:
    • ZooKeeper集群进入恢复模式的情况如下:
      • 集群在启动的过程中
      • Leader服务器出现网络中断、崩溃退出与重启等异常情况
      • 网络分区
      • leader失去大多数的follower
    • ZooKeeper集群进入到恢复模式之后,会:
      • 如果leader出现问题,那么重新选出一个新的leader。在选出leader之后,并且集群中过半的机器与leader完成状态同步时,集群会退出恢复模式进入到消息广播模式(ZooKeeper的选举算法有两种:Basic Paxos和Fast Paxos(默认))
      • 当新节点加入集群时,如果此时已经有leader负责进行消息广播,那么该节点进入到数据恢复模式,该节点会找到leader,然后与之进行同步,同步完成后,该节点进入到消息广播模式,参与到消息广播流程中
  • 消息广播模式:
    • zab的消息广播过程类似两阶段提交,但是移除了Abort逻辑,一个follower要么ack leader发来的proposal,要么不认这个leader
      • leader为事务生成一个全局的递增的事务ID(即ZXID),保证每个消息的因果顺序
      • leader为事务生成Proposal,并进行广播
      • leader会为每一个follower分配一个单独的队列,之后将需要广播的事务Proposal依次放入到这些队列中,然后根据FIFO策略进行发送(所以每个follower收到的事务序列都是相同的,可以保证严格的全局有序关系
      • follower收到leader发来的proposal之后,首先以日志形式写入磁盘,并在成功写入之后给leader发送ack响应
      • 当leader收到超过半数的follower的ack响应之后,leader自身也会完成对事务的提交(记录commit日志,并修改内存中的数据),然后广播一个commit消息给所有的follower。follower在收到commit消息之后,也会完成事务的提交

Zab的崩溃恢复需要保证以下两种情况(其他情况不做保证?):

  • 已经在leader上提交的事务,最终会被所有服务器接受
    1.jpg
server1是Leader,C2是在Leader上完成事务提交,但通知Follower服务器要Commit时挂掉,保证C2在Server2和Server3上提交
  • 丢弃那些对于只在leader上被提出的事务 1.jpg
假设初始的Leader服务器Server1在提出一个事务Proposal3(P3)之后,还没有给Follower发送请求,希望得到ack之前,挂了。则要丢弃P3事务



Zab选举算法的要求:

  • 旧Leader宕机后,选举新Leader,旧的Leader重启后不可能再次成为这次选举的新Leader
  • 旧Leader宕机后,在剩下的Follower服务器选取新Leader的标准,一定是事务ID最大的那个Follower成为新Leader(即数据同步最新的那台Follower服务器)
  • 事务ID(ZXID)是64位的数字。其中低32位可以靠做是一个简单的单调递增的计数器,高32位则代表一个Leader从生到死的epoch编号
  • 新Leader选举出来,从事务proposal中分析出旧Leader的epoch编号,并递增1,作为新的事务ID的高32位,然后新事务ID的低32位从0位重新开始计数
  • 新Leader通过事务ID和所有的Follower机器上的事务ID进行对比,确保数据同步。保证数据在所有的Follower上与之达成同步。旧Leader上新被提出的事务被抛弃。当数据达到同步,才将Follower服务器加入可用的Follower服务器列表。然后开始消息广播

参考文档

感谢浏览tim chow的作品!

如果您喜欢,可以分享到: 更多

如果您有任何疑问或想要与tim chow进行交流

可点此给tim chow发信

如有问题,也可在下面留言: