一、概述
1. 介绍
ZooKeeper 是一个开源的分布式协调服务,它的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。
原语:操作系统或计算机网络用语范畴。是由若干条指令组成的,用于完成一定功能的一个过程。具有不可分割性。即原语的执行必须是连续的,在执行过程中不允许被中断。
ZooKeeper 为我们提供了高可用、高性能、稳定的分布式数据一致性解决方案,通常被用于实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。
- 另外,ZooKeeper 将数据保存在内存中,性能是非常棒的。 在“读”多于“写”的应用程序中尤其地高性能,因为“写”会导致所有的服务器间同步状态。(“读”多于“写”是协调服务的典型场景)。
ZooKeeper由Yahoo开发,后来捐赠给了Apache,现已成为Apache顶级项目。ZooKeeper是一个开源的分布式应用程序协调服务器,其为分布式系统提供一致性服务。其一致性是通过基于Paxos算法的ZAB协议完成的。其主要功能包括:配置维护、分布式同步、集群管理、分布式事务等。
集群与分布式的区别:
比如,我现在有一个秒杀服务,并发量太大单机系统承受不住,那我加几台服务器也一样提供秒杀服务,这个时候就是Cluster集群。
但是,我现在换一种方式,我将一个秒杀服务拆分成多个子服务,比如创建订单服务,增加积分服务,扣优惠券服务等等,然后我将这些子服务都部署在不同的服务器上,这个时候就是Distributed分布式。
而我为什么反驳同学所说的分布式就是加机器呢?因为我认为加机器更加适用于构建集群,因为它真是只有加机器。而对于分布式来说,你首先需要将业务进行拆分,然后再加机器(不仅仅是加机器那么简单),同时你还要去解决分布式带来的一系列问题。
比如各个分布式组件如何协调起来,如何减少各个系统之间的耦合度,分布式事务的处理,如何去配置整个分布式系统等等。ZooKeeper主要就是解决这些问题的。
2. 工作机制:
3. 特点:
- Zookeeper:一个领导者(Leader),多个跟随者(Follower)组成的集群
- 集群中只要有半数以上节点存活,Zookeeper集群就能正常服务
- 单一系统映像 :每个Server保存一份相同的数据副本,无论客户端连到哪一个Server,数据都是一致的。
- 顺序一致性:从同一Client发起的事务请求,最终将会严格地按照发送顺序被应用到 ZooKeeper 中去。
- 原子性:所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,也就是说,要么整个集群中所有的机器都成功应用了某一个事务,要么都没有应用。
- 可靠性:一旦一次更改请求被应用,更改的结果就会被持久化,直到被下一次更改覆盖。
- 实时性:在一定时间范围内,Client能读到最新数据,即服务器同步数据副本的速度很快。
4. 应用场景:
- 统一命名服务
- 统一配置管理
- 统一集群管理
- 服务器动态上下线
- 软负载均衡
二、安装
使用docker安装
docker pull zookeeper:3.5.8
docker run -d --name zookeeper -p 2181:2181 zookeeper:3.5.8
配置文件
- tickTime=2000:通信心跳数,Zookeeper服务器与客户端心跳时间,单位毫秒
- Zookeeper使用的基本时间,服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个tickTime时间就会发送一个心跳,时间单位为毫秒。
- 它用于心跳机制,并且设置最小的session超时时间为两倍心跳时间(session的最小超时时间是2*tickTime)
- initLimit=10:LF初始通信时限
集群中的Follower服务器与Leader服务器之间初始连接时能容忍的最多心跳数(tickTime的数量),用它来限定集群中的zookeeper服务器连接到leader的时限。
- syncLimit=5:LF同步通信时限
集群中Leader与Follower之间的最大响应时间单位,假如响应超过syncLimit*tickTime,Leader认为Follower死掉,从服务器列表中删除Follower。
- dataDir:数据文件目录+数据持久化路径,主要用于保存zookeeper中的数据
- clientPort=2181:客户端连接端口,监听客户端连接的端口。
三、ZAB原子广播协议
1. Zookeeper架构
作为一个优秀高效且可靠的分布式协调框架,ZooKeeper在解决分布式数据一致性问题时并没有直接使用Paxos(paxos算法实现起来较难,且存在活锁和全序问题,无法保证两次最终提交的顺序),而是基于Fast Paxos专门定制了一致性协议叫做ZAB(ZooKeeper Automic Broadcast)原子广播协议,该协议能够很好地支持崩溃恢复。ZooKeeper使用单一主进程Leader用于处理客户端所有事务请求,即写请求。当服务器数据发生变更时,集群采用ZAB原子广播协议,Leader以事务提交proposal的形式广播到所有的副本进程,每一个事务分配一个全局的递增的事务编号xid。该过程遵循多数派原则,Leader得到超过半数的ack之后才会提交事务。 若客户端提交的请求为读请求时,则接受请求的节点直接根据自己保存的数据响应。若是写请求,且当前节点不是leader,那么该节点就会将请求转发给leader,leader会以提案的方式广播此写请求,如果超过半数的节点同意写请求,则该写请求就会提交。leader会通知所有的订阅者同步数据。
2. ZAB中的三个角色
和介绍Paxos一样,在介绍ZAB协议之前,我们首先来了解一下在ZAB中三个主要的角色,Leader 领导者、Follower跟随者、Observer观察者。
- Leader:集群中唯一的写请求处理者,能够发起投票(投票也是为了进行写请求),只有超过半数的节点同意才会提交该写请求。
- Follower:能够接收客户端的请求,如果是读请求则可以自己处理,如果是写请求则要转发给Leader。在选举leader及处理写请求过程中会参与投票,有选举权和被选举权。
- Observer:可以理解为没有投票权的Follower,主要职责是协助Follower处理读请求。【那么当整个zk集群读请求负载很高时,为什么不增加Follower节点呢?原因是增加Follower节点会让Leader在提出写请求提案时,需要半数以上的Follower投票节点同意,这样会增加Leader和Follower的通信压力,降低写操作效率。】
在ZAB协议中对zkServer(即上面我们说的三个角色的总称) 还有两种模式的定义,分别是消息广播和崩溃恢复。
3. 消息广播模式
一旦进入广播模式,集群中非leader节点接受到事务请求,首先会将事务请求转发给服务器,leader服务器为其生成对应的事务提案proposal,并发送给集群中其他节点,如果过半则事务提交
说白了就是ZAB协议是如何处理写请求的,上面我们不是说只有Leader能处理写请求嘛?那么我们的Follower和Observer是不是也需要同步更新数据呢?总不能数据只在Leader中更新了,其他角色都没有得到更新吧?
不就是在整个集群中保持数据的一致性嘛?如果是你,你会怎么做呢?
废话,第一步肯定需要Leader将写请求广播出去呀,让Leader问问Followers是否同意更新,如果超过半数以上的同意那么就进行Follower和Observer的更新(和Paxos一样)。当然这么说有点虚,画张图理解一下。
嗯。。。看起来很简单,貌似懂了🤥🤥🤥。这两个Queue哪冒出来的?答案是ZAB需要让Follower和Observer保证顺序性。何为顺序性,比如我现在有一个写请求A,此时Leader将请求A广播出去,因为只需要半数同意就行,所以可能这个时候有一个FollowerF1因为网络原因没有收到,而Leader又广播了一个请求B,因为网络原因,F1竟然先收到了请求B然后才收到了请求A,这个时候请求处理的顺序不同就会导致数据的不同,从而产生数据不一致问题。
所以在Leader这端,它为每个其他的zkServer准备了一个队列,采用先进先出的方式发送消息。由于协议是 通过TCP来进行网络通信的,保证了消息的发送顺序性,接受顺序性也得到了保证。
除此之外,在ZAB中还定义了一个全局单调递增的事务IDZXID,它是一个64位long型,其中高32位表示epoch年代,低32位表示事务id。epoch是会根据Leader的变化而变化的,当一个Leader挂了,新的Leader上位的时候,年代(epoch)就变了。而低32位可以简单理解为递增的事务id。 定义这个的原因也是为了顺序性,每个proposal在Leader中生成后需要通过其ZXID来进行排序,才能得到处理。编号的大小体现事务操作的先后顺序
以上步骤可总结为:
- leader接受到消息后,消息通过全局唯一的64位自增事务id,zxid标识
- leader发送给follower的提案是有序的,leader会创建一个FIFO队列,将提案顺序写入队列中发送给follower
- follower接受到提案后,会比较提案zxid和本地事务日志最大的zxid,若提案zxid比本地事务id大,将提案记录到本地日志中,反馈ack给leader,否则拒绝
- leader接收到过半ack后,leader向所有的follower发送commit,通知每个follower执行本地事务
4. 崩溃恢复模式
当服务启动或Leader崩溃后,zk进入恢复状态,选举leader,leader选出后,将完成leader和其他机器的数据同步,当大多数server完成和leader的同步后,恢复模式结束
说到崩溃恢复我们首先要提到ZAB中的Leader选举算法,当系统出现崩溃影响最大应该是Leader的崩溃,因为我们只有一个Leader,所以当Leader出现问题的时候我们势必需要重新选举Leader。
Leader选举可以分为两个不同的阶段,第一个是我们提到的Leader宕机需要重新选举,第二则是当Zookeeper启动时需要进行系统的Leader初始化选举。下面我先来介绍一下ZAB是如何进行初始化选举的。
4.1 初始化选举
三个原则: 1)zookeeper集群中只有超过了半数以上的服务器启动,集群才能正常工作 2)在集群正常工作之前,myid小的服务器会给myid大的服务器进行投票,持续到集群正常工作,选出leader 3)选择leader之后,之前的服务器状态由looking改变为following,以后的服务器都是follower
假设我们集群中有3台机器,那也就意味着我们需要两台以上同意(超过半数)。比如这个时候我们启动了server1,它会首先投票给自己,投票内容为服务器的myid和ZXID,因为初始化所以ZXID都为0,此时server1发出的投票为 (1,0)。但此时server1的投票仅为1,所以不能作为Leader,此时还在选举阶段所以整个集群处于Looking状态。
接着server2启动了,它首先也会将投票选给自己(2,0),并将投票信息广播出去(server1也会,只是它那时没有其他的服务器了),server1在收到server2的投票信息后会将投票信息与自己的作比较。首先它会比较ZXID,ZXID大的优先为Leader,如果相同则比较myid,myid大的优先作为Leader。所以此时server1发现server2更适合做Leader,它就会将自己的投票信息更改为(2,0)然后再广播出去,之后server2收到之后发现和自己的一样无需做更改,并且自己的投票已经超过半数,则确定server2为Leader,server1也会将自己服务器设置为Following变为Follower。整个服务器就从Looking变为了正常状态。
当server3启动发现集群没有处于Looking状态时,它会直接以Follower的身份加入集群。
4.2 leader宕机重新选举
还是前面三个server的例子,如果在整个集群运行的过程中server2挂了,那么整个集群会如何重新选举Leader呢?其实和初始化选举差不多。
首先毫无疑问的是剩下的两个Follower会将自己的状态从Following变为Looking状态,然后每个server会向初始化投票一样首先给自己投票(这不过这里的zxid可能不是0了,这里为了方便随便取个数字)。
假设server1给自己投票为(1,99),然后广播给其他server,server3首先也会给自己投票(3,95),然后也广播给其他server。server1和server3此时会收到彼此的投票信息,和一开始选举一样,他们也会比较自己的投票和收到的投票(zxid大的优先,如果相同那么就myid大的优先)。这个时候server1收到了server3的投票发现没自己的合适故不变,server3收到server1的投票结果后发现比自己的合适于是更改投票为(1,99)然后广播出去,最后server1收到了发现自己的投票已经超过半数就把自己设为Leader,server3也随之变为Follower。
请注意ZooKeeper为什么要设置奇数个结点?比如这里我们是三个,挂了一个我们还能正常工作,挂了两个我们就不能正常工作了(已经没有超过半数的节点数了,所以无法进行投票等操作了)。而假设我们现在有四个,挂了一个也能工作,但是挂了两个也不能正常工作了,这是和三个一样的,而三个比四个还少一个,带来的效益是一样的,所以Zookeeper推荐奇数个server。
那么说完了ZAB中的Leader选举方式之后我们再来了解一下崩溃恢复是什么玩意?
其实主要就是当集群中有机器挂了,我们整个集群如何保证数据一致性?
如果只是Follower挂了,而且挂的没超过半数的时候,因为我们一开始讲了在Leader中会维护队列,所以不用担心后面的数据没接收到导致数据不一致性。
如果Leader挂了那就麻烦了,我们肯定需要先暂停服务变为Looking状态然后进行Leader的重新选举(上面我讲过了),但这个就要分为两种情况了,分别是确保已经被Leader提交的提案最终能够被所有的Follower提交和跳过那些已经被丢弃的提案。
确保已经被Leader提交的提案最终能够被所有的Follower提交是什么意思呢?
假设Leader (server2)发送commit请求(忘了请看上面的消息广播模式),他发送给了server3,然后要发给server1的时候突然挂了。这个时候重新选举的时候我们如果把server1作为Leader的话,那么肯定会产生数据不一致性,因为server3肯定会提交刚刚server2发送的commit请求的提案,而server1根本没收到所以会丢弃。
那怎么解决呢?
聪明的同学肯定会质疑,这个时候server1已经不可能成为Leader了,因为server1和server3进行投票选举的时候会比较ZXID,而此时server3的ZXID肯定比server1的大了。(不理解可以看前面的选举算法)
那么跳过那些已经被丢弃的提案又是什么意思呢?
假设Leader (server2)此时同意了提案N1,自身提交了这个事务并且要发送给所有Follower要commit的请求,却在这个时候挂了,此时肯定要重新进行Leader的选举,比如说此时选server1为Leader(这无所谓)。但是过了一会,这个挂掉的Leader又重新恢复了,此时它肯定会作为Follower的身份进入集群中,需要注意的是刚刚server2已经同意提交了提案N1,但其他server并没有收到它的commit信息,所以其他server不可能再提交这个提案N1了,这样就会出现数据不一致性问题了,所以该提案N1最终需要被抛弃掉。
四、重要概念
1. Data model(数据模型)
- ZooKeeper 数据模型采用层次化的多叉树形结构,每个节点上都可以存储数据,这些数据可以是数字、字符串或者是二级制序列。并且。每个节点还可以拥有 N 个子节点,最上层是根节点以“/”来代表。每个数据节点在 ZooKeeper 中被称为znode,它是 ZooKeeper 中数据的最小单元。并且,每个 znode 都一个唯一的路径标识。
- 强调一句:ZooKeeper 主要是用来协调服务的,而不是用来存储业务数据的,所以不要放比较大的数据在 znode 上,ZooKeeper 给出的上限是每个结点的数据大小最大是 1M。
- 从下图可以更直观地看出:ZooKeeper 节点路径标识方式和 Unix 文件系统路径非常相似,都是由一系列使用斜杠”/“进行分割的路径表示,开发人员可以向这个节点中写入数据,也可以在节点下面创建子节点。这些操作我们后面都会介绍到。
2. znode(数据节点)
介绍了 ZooKeeper 树形数据模型之后,我们知道每个数据节点在 ZooKeeper 中被称为znode,它是 ZooKeeper 中数据的最小单元。你要存放的数据就放在上面,是你使用 ZooKeeper 过程中经常需要接触到的一个概念。
2.1. znode 4种类型
我们通常是将 znode 分为 4 大类:
- 持久(PERSISTENT)节点:一旦创建就一直存在即使 ZooKeeper 集群宕机,直到将其删除。
- 临时(EPHEMERAL)节点:临时节点的生命周期是与客户端会话(session)绑定的,会话消失则节点消失。并且,临时节点只能做叶子节点,不能创建子节点。(可以用来判断客户端是否在线)
- 持久顺序(PERSISTENT_SEQUENTIAL)节点:除了具有持久(PERSISTENT)节点的特性之外, 子节点的名称还具有顺序性。比如/node1/app0000000001、/node1/app0000000002。
- 临时顺序(EPHEMERAL_SEQUENTIAL)节点:除了具备临时(EPHEMERAL)节点的特性之外,子节点的名称还具有顺序性。
创建znode时设置顺序标识,znode名称后会附加一个值,顺序号是一个单调递增的计数器,由父节点维护。 在分布式系统中,顺序号可以用于为所有的事件进行全局排序,这样客户端可以通过顺序号推断事件的顺序。
2.2. znode 数据结构
每个 znode 由 2 部分组成:
- stat:状态信息
- data:节点存放的数据的具体内容
如下所示,我通过 get 命令来获取 根目录下的 dubbo 节点的内容。(get 命令在下面会介绍到)。
[zk: 127.0.0.1:2181(CONNECTED) 6] get -s /dubbo
# 该数据节点关联的数据内容为空
null
# 下面是该数据节点的一些状态信息,其实就是 Stat 对象的格式化输出
cZxid = 0x2
ctime = Tue Nov 27 11:05:34 CST 2018
mZxid = 0x2
mtime = Tue Nov 27 11:05:34 CST 2018
pZxid = 0x3
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1C
Stat 类中包含了一个数据节点的所有状态信息的字段,包括事务 ID-cZxid、节点创建时间-ctime 和子节点个数-numChildren 等等。
下面我们来看一下每个 znode 状态信息究竟代表的是什么吧!(下面的内容来源于《从 Paxos 到 ZooKeeper 分布式一致性原理与实践》,因为 Guide 确实也不是特别清楚,要学会参考资料的嘛! ) :
znode 状态信息 | 解释 |
---|---|
cZxid | create ZXID,即该数据节点被创建时的事务 id |
ctime | create time,即该节点的创建时间 |
mZxid | modified ZXID,即该节点最终一次更新时的事务 id |
mtime | modified time,即该节点最后一次的更新时间 |
pZxid | 该节点的子节点列表最后一次修改时的事务 id,只有子节点列表变更才会更新 pZxid,子节点内容变更不会更新 |
cversion | 子节点版本号,当前节点的子节点每次变化时值增加 1 |
dataVersion | 数据节点内容版本号,节点创建时为 0,每更新一次节点内容(不管内容有无变化)该版本号的值增加 1 |
aclVersion | 节点的 ACL 版本号,表示该节点 ACL 信息变更次数 |
ephemeralOwner | 创建该临时节点的会话的 sessionId;如果当前节点为持久节点,则 ephemeralOwner=0 |
dataLength | 数据节点内容长度 |
numChildren | 当前节点的子节点个数 |
3. 版本(version)
在前面我们已经提到,对应于每个 znode,ZooKeeper 都会为其维护一个叫作Stat的数据结构,Stat 中记录了这个 znode 的三个相关的版本:
- dataVersion:当前 znode 节点的版本号
- cversion: 当前 znode 子节点的版本
aclVersion: 当前 znode 的 ACL 的版本。
4. ACL(权限控制)
ZooKeeper 采用 ACL(AccessControlLists)策略来进行权限控制,类似于 UNIX 文件系统的权限控制。
对于 znode 操作的权限,ZooKeeper 提供了以下 5 种:CREATE: 能创建子节点
- READ:能获取节点数据和列出其子节点
- WRITE: 能设置/更新节点数据
- DELETE: 能删除子节点
- ADMIN: 能设置节点 ACL 的权限
其中尤其需要注意的是,CREATE和DELETE这两种权限都是针对子节点的权限控制。
对于身份认证,提供了以下几种方式:
- world:默认方式,所有用户都可无条件访问。
- auth:不使用任何 id,代表任何已认证的用户。
- digest:用户名:密码认证方式:username:password。
- ip:对指定 ip 进行限制。
5. Watcher(事件监听器)
Watcher(事件监听器),是 ZooKeeper 中的一个很重要的特性。ZooKeeper 允许用户在指定节点上注册一些 Watcher,并且在一些特定事件触发的时候,ZooKeeper 服务端会将事件通知到感兴趣的客户端上去,该机制是 ZooKeeper 实现分布式协调服务的重要特性。
监听器原理:
- 首先要有一个main线程
- 在main线程中启动zookeeper客户端,这时就会创建两个线程,一个负责网络连接通信(connect),一个负责监听(listener)
- 通过connect线程将注册的监听事件发送给zookeeper
- 在zookeeper的注册监听器列表中将注册的监听事件添加到列表中
- zookeeper监听到数据或者路径变化,就会将这个信息发送给listener线程
- listener线程内部调用process()方法(业务处理方法,程序员编写)
常见的监听:
- 监听节点数据的变化
get -w path
- 监听子节点增减的变化
ls -w path
6. 会话(Session)
Session 可以看作是 ZooKeeper 服务器与客户端的之间的一个 TCP 长连接,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够向 ZooKeeper 服务器发送请求并接受响应,同时还能够通过该连接接收来自服务器的 Watcher 事件通知。
Session 有一个属性叫做:sessionTimeout,sessionTimeout代表会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在sessionTimeout规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。
另外,在为客户端创建会话之前,服务端首先会为每个客户端都分配一个sessionID。由于sessionID是 ZooKeeper 会话的一个重要标识,许多与会话相关的运行机制都是基于这个sessionID的,因此,无论是哪台服务器为客户端分配的sessionID,都务必保证全局唯一。7. 选举机制
- 半数机制:集群中半数以上机器存活,集群可用。所以zookeeper适合安装奇数台服务器
- zookeeper虽然在配置文件中并没有指定Master和Slave。但是,zookeeper工作时,是有一个节点为Leader,其他为Follower,Leader是通过内部的选举机制临时产生的。
- 比如:目前有5台服务器,每台服务器均没有数据,它们的编号分别是1,2,3,4,5,按编号依次启动,它们的选择举过程如下:
- 服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking(选举状态)。
- 服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。
- 服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟。
- 服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的编号大,但之前服务器3已经胜出,所以服务器4只能成为小弟。
- 服务器5启动,后面的逻辑同服务器4成为小弟。
8. 写数据流程
五、ZooKeeper 集群
为了保证高可用,最好是以集群形态来部署 ZooKeeper,这样只要集群中大部分机器是可用的(能够容忍一定的机器故障),那么 ZooKeeper 本身仍然是可用的。通常 3 台服务器就可以构成一个 ZooKeeper 集群了。ZooKeeper 官方提供的架构图就是一个 ZooKeeper 集群整体对外提供服务。
上图中每一个 Server 代表一个安装 ZooKeeper 服务的服务器。组成 ZooKeeper 服务的服务器都会在内存中维护当前的服务器状态,并且每台服务器之间都互相保持着通信。集群间通过 ZAB 协议(ZooKeeper Atomic Broadcast)来保持数据的一致性。
最典型集群模式: Master/Slave 模式(主备模式)。在这种模式中,通常 Master 服务器作为主服务器提供写服务,其他的 Slave 服务器从服务器通过异步复制的方式获取 Master 服务器最新的数据提供读服务。1. ZooKeeper 集群角色
但是,在 ZooKeeper 中没有选择传统的 Master/Slave 概念,而是引入了 Leader、Follower 和 Observer 三种角色。如下图所示
ZooKeeper 集群中的所有机器通过一个Leader 选举过程来选定一台称为 “Leader” 的机器,Leader 既可以为客户端提供写服务又能提供读服务。除了 Leader 外,Follower和Observer都只能提供读服务。Follower 和 Observer 唯一的区别在于 Observer 机器不参与 Leader 的选举过程,也不参与写操作的“过半写成功”策略,因此 Observer 机器可以在不影响写性能的情况下提升集群的读性能。
角色 | 说明 |
---|---|
Leader | 为客户端提供读和写的服务,负责投票的发起和决议,更新系统状态。 |
Follower | 为客户端提供读服务,如果是写服务则转发给 Leader。在选举过程中参与投票。 |
Observer | 为客户端提供读服务,如果是写服务则转发给 Leader。不参与选举过程中的投票,也不参与“过半写成功”策略。在不影响写性能的情况下提升集群的读性能。此角色于 ZooKeeper3.3 系列新增的角色。 |
当 Leader 服务器出现网络中断、崩溃退出与重启等异常情况时,就会进入 Leader 选举过程,这个过程会选举产生新的 Leader 服务器。
这个过程大致是这样的:
- Leader election(选举阶段):节点在一开始都处于选举阶段,只要有一个节点得到超半数节点的票数,它就可以当选准 leader。
- Discovery(发现阶段):在这个阶段,followers 跟准 leader 进行通信,同步 followers 最近接收的事务提议。
- Synchronization(同步阶段):同步阶段主要是利用 leader 前一阶段获得的最新提议历史,同步集群中所有的副本。同步完成之后 准 leader 才会成为真正的 leader。
- Broadcast(广播阶段):到了这个阶段,ZooKeeper 集群才能正式对外提供事务服务,并且 leader 可以进行消息广播。同时如果有新的节点加入,还需要对新节点进行同步。
2. ZooKeeper 集群中的服务器状态
- LOOKING:寻找 Leader。
- LEADING:Leader 状态,对应的节点为 Leader。
- FOLLOWING:Follower 状态,对应的节点为 Follower。
- OBSERVING:Observer 状态,对应节点为 Observer,该节点不参与 Leader 选举。
3. ZooKeeper 集群为啥最好奇数台?
ZooKeeper 集群在宕掉几个 ZooKeeper 服务器之后,如果剩下的 ZooKeeper 服务器个数大于宕掉的个数的话整个 ZooKeeper 才依然可用。假如我们的集群中有 n 台 ZooKeeper 服务器,那么也就是剩下的服务数必须大于 n/2。先说一下结论,2n 和 2n-1 的容忍度是一样的,都是 n-1,大家可以先自己仔细想一想,这应该是一个很简单的数学问题了。 比如假如我们有 3 台,那么最大允许宕掉 1 台 ZooKeeper 服务器,如果我们有 4 台的的时候也同样只允许宕掉 1 台。 假如我们有 5 台,那么最大允许宕掉 2 台 ZooKeeper 服务器,如果我们有 6 台的的时候也同样只允许宕掉 2 台。
综上,何必增加那一个不必要的 ZooKeeper 呢?