学习目标
-
Zookeeper集群中的Leader选举
-
Zookeeper集群中的数据同步
第1章 集群简介
前面第1篇文章讲过Zookeeper是作为分布式环境下的协调器而出生的,既然它是在分布式环境使用的,那它本身也要做集群才能保证高可用性。在前文中也讲过了源码的集群部署和Centos7系统下的集群部署,那么这里我们先通过一张图来大概了解一下集群时的启动和单机版的启动的不同之处:
从这张图上,我们可以看出来,集群启动和单机启动的最大不同就是集群启动的时候会配置选举的相关参数,以及启动选举策略。这其实说白了,就是解决在集群环境下如何保证数据的一致性问题的。
本文将从两个方面来学习Zookeeper的集群:1、Zookeeper集群是如何做数据同步的。2、Zookeeper中leader挂掉了如何进行选举。明确了这两个点,那我们Zookeeper的数据一致性就不存在问题了。
接下来我们先了解一下集群中有哪几种角色。其实我们之前学过redis集群或者mysql集群等知识,大体的也能猜到,既然要做集群,不同的节点肯定有自己的身份。那在zookeeper的集群中,实际上分为三种角色:分别是Leader、Follower、Observer。
1.1 集群角色
1、Leader:是整个zookeeper集群的核心,一个集群中只存在一个Leader节点,它主要的工作任务有两项
-
事务请求的唯一调度和处理者,保证集群事务处理的顺序性
-
集群内部各服务器的调度者
2、Follower:主要职责是
-
处理客户端非事物请求、转发事物请求给leader服务器
-
参与事物请求Proposal的投票(需要半数以上服务器通过才能通知leader commit数据; Leader发起的提案,要求Follower投票)
-
参与Leader选举的投票
3、Observer:zookeeper3.3开始引入的一个全新的服务器角色,从字面来理解,该角色充当了观察者的角色。
观察zookeeper集群中的最新状态变化并将这些状态变化同步到observer服务器上。Observer的工作原理与follower角色基本一致,而它和follower角色唯一的不同在于observer不参与任何形式的投票,包括事物请求Proposal的投票和leader选举的投票。简单来说,observer服务器只提供非事物请求服务,通常在于不影响集群事物处理能力的前提下提升集群非事物处理的能力。
接下来再分析一下,在zookeeper集群中,各节点是遵循什么样的协议。
1.2 ZAB协议
1.2.1 概念
全称 Zookeeper Atomic Broadcast(Zookeeper 原子广播协议)。它是专门为Zookeeper设计的一种支持崩溃恢复和原子广播的协议。
ZAB协议包括两种基本的模式:崩溃恢复和消息广播
当整个服务框架在启动过程中,或是当Leader服务器出现网络中断崩溃退出与重启等异常情况时,ZAB就会进入恢复模式并选举产生新的Leader服务器。
当选举产生了新的Leader服务器,同时集群中已经有过半的机器与该Leader服务器完成了状态同步之后,ZAB协议就会退出崩溃恢复模式,进入消息广播模式。
当有新的服务器加入到集群中去,如果此时集群中已经存在一个Leader服务器在负责进行消息广播,那么新加入的服务器会自动进入数据恢复模式,找到Leader服务器,并与其进行数据同步,然后一起参与到消息广播流程中去。
以上其实大致经历了三个步骤:
-
崩溃恢复:主要就是Leader选举过程
-
数据同步:Leader服务器与其他服务器进行数据同步
-
消息广播:Leader服务器将数据发送给其他服务器
1.2.2 结论
ZooKeeper保证的是CP
-
ZooKeeper不能保证每次服务请求的可用性。(注:在极端环境下,ZooKeeper可能会丢弃一些请求,消费者程序需要重新请求才能获得结果)。所以说,ZooKeeper不能保证服务可用性。
-
进行Leader选举时集群都是不可用
上面讲了ZAB协议是Zookeeper集群中进行数据同步的基本协议,我们在学习Zookeeper的时候经常会听到ZAB协议和Paxos算法,实际上这是两个不同的东西,Zookeeper是基于ZAB协议去做的数据同步,而ZAB协议底层又是通过Paxos算法去完成Leader选举的,说准确点,应该是FastLeaderElection算法去进行Leader选举的,但FastLeaderElection算法又是典型的Paxos算法,所以我们先来了解下Paxos算法,这样更有助于掌握FastLeaderElection算法。
1.3 Paxos算法
1.3.1 介绍
分布式事务中常见的事务模型有2PC和3PC,无论是2PC提交还是3PC提交都无法彻底解决分布式的一致性问题。这个在之前讲Seata的时候已经介绍过。Google Chubby的作者Mike Burrows说过,世上只有一种一致性算法,那就是Paxos,所有其他一致性算法都是Paxos算法的不完整版,如Chubby, Raft,ZAB,微信的PhxPaxos等。Paxos算法是莱斯利·兰伯特(Leslie Lamport)1990年提出的一种基于消息传递的一致性算法,它曾就此发表了《The Part-Time Parliament》,《Paxos Made Simple》,由于采用故事的方式来解释此算法,感觉还是很难理解。
1.3.2 算法理解
Paxos 算法是分布式一致性算法用来解决一个分布式系统如何就某个值(决议)达成一致的问题。
一个典型的场景是,在一个分布式数据库系统中,如果各节点的初始状态一致,每个节点都执行相同的操作序列,那么他们最后能得到一个一致的状态。为保证每个节点执行相同的命令序列,需要在每一条指令上执行一个”一致性算法”以保证每个节点看到的指令一致。 分布式系统中一般是通过多副本来保证可靠性,而多个副本之间会存在数据不一致的情况。所以必须有一个一致性算法来保证数据的一致。
1.3.3 相关概念
在Paxos算法中,有三种角色:
-
Proposer
-
Acceptor
-
Learners
在具体的实现中,一个进程可能同时充当多种角色。比如一个进程可能既是Proposer又是Acceptor又是Learner。Proposer负责提出提案,Acceptor负责对提案作出裁决(accept与否),learner负责学习提案结果。 还有一个很重要的概念叫提案(Proposal)。最终要达成一致的value就在提案里。只要Proposer发的提案被Acceptor接受(半数以上的Acceptor同意才行),Proposer就认为该提案里的value被选定了。Acceptor告诉Learner哪个value被选定,Learner就认为那个value被选定。只要Acceptor接受了某个提案,Acceptor就认为该提案里的value被选定了。 为了避免单点故障,会有一个Acceptor集合,Proposer向Acceptor集合发送提案,Acceptor集合中的每个成员都有可能同意该提案且每个Acceptor只能批准一个提案,只有当一半以上的成员同意了一个提案,就认为该提案被选定了。
1.3.4 算法流程
Propser有两个重要属性,提案编号N, 提案V, 简记 Proposer(N, V)。
Acceptor有三个重要属性,响应提案编号ResN, 接受的提案编号AcceptN, 接收的提案AcceptV, 间记Acceptor(ResN, AcceptN, AcceptV)。
1、第一阶段: Prepare准备阶段
Proposer: Proposer生成全局唯一且递增的提案编号N,,向所有Acceptor发送Prepare请求,这里无需携带提案内容,只携带提案编号即可, 即发送 Proposer(N, null)。
Acceptor: Acceptor收到Prepare请求后,有两种情况:
-
如果Acceptor首次接收Prepare请求, 设置ResN=N, 同时响应ok
-
如果Acceptor不是首次接收Prepare请求,则:
-
若请求过来的提案编号N小于等于上次持久化的提案编号ResN,则不响应或者响应error。
-
若请求过来的提案编号N大于上次持久化的提案编号ResN, 则更新ResN=N,同时给出响应。响应的结果有两种,如果这个Acceptor此前没有接受过提案, 只返回ok。否则如果这个Acceptor此前接收过提案,则返回ok和上次接受的提案编号AcceptN, 接收的提案AcceptV。
-
2、第二阶段: Accept接受阶段
Proposer: Proposer收到响应后,有两种情况:
-
如果收到了超过半数响应ok, 检查响应中是否有提案,如果有的话,取提案V=响应中最大AcceptN对应的AcceptV,如果没有的话,V则有当前Proposer自己设定。最后发出accept请求,这个请求中携带提案V。
-
如果没有收到超过半数响应ok, 则重新生成提案编号N, 重新回到第一阶段,发起Prepare请求。
Acceptor: Acceptor收到accept请求后,分为两种情况:
-
如果发送的提案请求N大于此前保存的RespN,接受提案,设置AcceptN = N, AcceptV=V, 并且回复ok。
-
如果发送的提案请求N小于等于此前保存的RespN,不接受,不回复或者回复error。
Proposer: Proposer收到ok超过半数,则V被选定,否则重新发起Prepare请求。
3、第三阶段: Learn学习阶段
Learner: Proposer收到多数Acceptor的Accept后,决议形成,将形成的决议发送给所有Learner。
第2章 Leader选举
接下来我们就来分析一下Leader的选举和数据同步的大体流程和原理,先来看张图:
2.1 startLeaderElection
从上面的图可以看出来,当每台服务器启动后,会启动一个QuorumPeer线程,来看看QuorumPeer的start方法
public synchronized void start() {
if (!getView().containsKey(myid)) {
throw new RuntimeException("My id " + myid + " not in the peer list");
}
//加载数据:数据引擎( ZKDatabase )负责存储/加载/查找数据(基于目录树结构的KV+操作日志+客户端Session);
loadDataBase();
//启动网络通信组件:负责维护与客户端的连接(接收客户端的请求并发送相应的响应)
startServerCnxnFactory();
try {
//启动控制台
adminServer.start();
} catch (AdminServerException e) {
LOG.warn("Problem starting AdminServer", e);
System.out.println(e);
}
//开启选举协调者,并执行选举(这个过程是会持续,并不是一次操作就结束了)
startLeaderElection();
//执行QuorumPeer线程的run方法
super.start();
}
本章内容只讲Leader选举,所以我们直接进入startLeaderElection方法
synchronized public void st