四、Zookeeper
Zookeeper入门看这篇就够了_java_66666的博客-CSDN博客_zookeeper
Zookeeper 3、Zookeeper工作原理(详细) - Bodi - 博客园 (cnblogs.com)
1、简介
ZooKeeper最早起源于雅虎研究院的一个研究小组,在立项初期,发现很多项目都是用动物的名字来起的,当时首席科学家觉得不能再继续起动物的名字了,把它起名叫动物园管理员,正好它分布式协同服务的特性很相符,所以ZooKeeper诞生了。
顾名思义 zookeeper 就是动物园管理员,他是用来管 hadoop(大象)、Hive(蜜蜂)、pig(小 猪)的管理员, Apache Hbase 和 Apache Solr 的分布式集群都用到了 zookeeper
- ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务 ,是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。
- Zookeeper的设计目标是将那些复杂容易出错的分布式一致性服务封装起来,构成一个高效可用的原语集,并提供一系列简单接口给用户使用。
上面的解释有点抽象,简单来说 zookeeper=文件系统+监听通知机制。
chubby与Zookeeper:
- chubby 是 google 的,完全实现 paxos 算法,不开源。
- zookeeper 是 chubby的开源实现,使用 zab 协议,paxos 算法的变种。
2、Zookeeper提供了什么(实质)
zookeeper = 文件系统 + 监听通知机制
1)文件系统
- Zookeeper提供一个多层级的节点命名空间(节点称为znode)。维护一个类似文件系统的数据结构
- 与文件系统不同的是,这些节点 都可以设置 关联的数据 ,而文件系统中只有文件节点可以存放数据而目录节点不行。
- Zookeeper为了保证高吞吐和低延 迟,在内存中维护了这个树状的目录结构,这种特性使得Zookeeper 不能用于存放大量的数据 ,每个节点的存放数据上限为 1M 。
四种类型的znode:
-
PERSISTENT-持久化目录节点
客户端与zookeeper断开连接后,该节点依旧存在
-
PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点
客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
-
EPHEMERAL-临时目录节点
客户端与zookeeper断开连接后,该节点被删除
-
EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点
客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
2)监听通知机制
client端会对某个znode建立一个watcher监听事件 ,当该znode发生变化时(数据改变、被删除、子目录节点增加删除),这些client会收到zookeeper的通知, 然后client可以根据znode变化来做出业务上的改变等。
3、ZooKeeper 特点
- 顺序一致性: 从同一客户端发起的事务请求,最终将会严格地按照顺序被应用到 ZooKeeper 中去。
- 原子性: 所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,也就是说,要么整个集群中所有的机器都成功应用了某一个事务,要么都没有应用。一次数据更新要么成功,要么失败
- 单一系统映像(全局唯一数据视图) : 无论客户端连到哪一个 ZooKeeper 服务器上,其看到的服务端数据模型都是一致的。
- 可靠性: 一旦一次更改请求被应用,更改的结果就会被持久化,直到被下一次更改覆盖。
zookeeper是如何保证事务的顺序一致性的?
ZooKeeper状态的每一次改变, 都对应着一个递增的Transaction id, 该id称为zxid. 由于zxid的递增性质, 如果zxid1小于zxid2, 那么zxid1肯定先于zxid2发生。创建任意节点, 或者更新任意节点的数据, 或者删除任意节点, 都会导致Zookeeper状态发生改变, 从而导致zxid的值增加.
zookeeper采用了 递增的事务 Id 来标识,所有的proposal(提议)都在被提出的时候加上了zxid,zxid实际 上是一个64位的数字,高32位是epoch(时期; 纪元; 世; 新时代)用来标识leader是否发生改变,如果有 新的leader产生出来,epoch会自增, 低 32 位用来递增计数 。当新产生proposal的时候,会依据数据库的 两阶段过程,首先会向其他的server发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就 会开始执行。
4、Zookeeper的应用场景(能做什么)
-
命名服务——文件系统
命名服务是指通过指定的名字来获取资源或者服务的地址,利用zk创建一个全局的路径,即是唯一的路径,这个路径就可以作为一个名字,指向集群中的集群,提供的服务的地址,或者一个远程的对象等等。
-
配置管理——文件系统、通知机制
假设我们的程序是分布式部署在多台机器上,如果我们要改变程序的配置文件,需要逐台机器去修改,非常麻烦。
现在把这些配置全部放到zookeeper上去,配置信息保存在 zookeeper 的某个目录节点znode中,然后所有相关应用程序对这个目录节点进行监听,一旦该znode上保存的配置信息发生变化,每个应用程序就会收到 zookeeper (watcher)的通知,然后从 zookeeper 获取新的配置信息应用到系统中。
-
集群管理——文件系统、通知机制
所谓集群管理无在乎两点:是否有机器退出和加入、选举master。
- 对于第一点,所有机器约定在父目录下创建临时目录节点,然后监听父目录节点的子节点变化消息。一旦有机器挂掉,该机器与 zookeeper的连接断开,其所创建的临时目录节点被删除,所有其他机器都收到通知:某个兄弟目录被删除,于是,所有人都知道:它上船了。新机器加入也是类似,所有机器收到通知:新兄弟目录加入,highcount又有了,
- 对于第二点,我们稍微改变 一下,所有机器创建临时顺序编号目录节点,每次选取编号最小的机器作为 master 就好。
-
分布式锁——文件系统、通知机制
Zookeeper 提供两种锁:独占锁、共享锁。独占锁即一次只能有一个线程使用资源,共享锁是读锁共享,读写互斥,即可以有多线线程同时读同一个资源,如果要使用写锁也只能有一个线程使用。Zookeeper 可以对分布式锁进行控制。
有了zookeeper的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一个是保持独占,另一个是控制时序 。
- 对于第一类,我们将zookeeper上的一个znode看作是一把锁,通过createznode的方式来实现。所有客户 端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的 distribute_lock 节点就释放出锁。(惊群)
- 对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选 master一样, 编号最小的获得锁 ,用完删除,依次方便。
-
队列管理——文件系统、通知机制
两种类型的队列:
- 同步队列,当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。在约定目录下创建临时目录节点,监听节点数目是否是我们要求的数目。
- 队列按照 FIFO 方式进行入队和出队操作。和分布式锁服务中的控制时序场景基本原理一致,创建临时顺序编号目录节点,入列有编号,出列按编号。在特定的目录下创建 PERSISTENT_SEQUENTIAL 节点,创建成功时Watcher 通知等待的队列,队列删除序列号最小的节点用以消费。此场景下Zookeeper 的 znode 用于消息存储,znode 存储的数据就是消息队列中的消息内容,SEQUENTIAL 序列号就是消息的编号,按序取出即可。由于创建的节点是持久化的,所以不必担心队列消息的丢失问题。
5、ZooKeeper的选举机制(角色、状态、选举过程)
Zookeeper服务器的角色
1、Leader
(1)事务请求的唯一调度和处理者,保证集群事务处理的顺序性
(2)集群内部各服务的调度者
2、Follower
(1)处理客户端的非事务请求,转发事务请求给 Leader 服务器
(2)参与事务请求 Proposal 的投票
(3)参与 Leader 选举投票
3、Observer
(1)3.0 版本以后引入的一个服务器角色,在不影响集群事务处理能力的基础上提升集群的非事务处理能力
(2)处理客户端的非事务请求,转发事务请求给 Leader 服务器
(3)不参与任何形式的投票
Observer可以接受客户端连接,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度。
机器中为什么会有leader?
在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行, 其他的机器可以共享这个结果 ,这样可 以大大 减少重复计算 , 提高性能 ,于是就需要进行leader选举。
Zookeeper服务器的工作状态
服务器具有四种状态,分别是 LOOKING、FOLLOWING、LEADING、OBSERVING。
(1)LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。
(2)FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。
(3)LEADING:领导者状态。表明当前服务器角色是 Leader。
(4)OBSERVING:观察者状态。表明当前服务器角色是 Observer。
Zookeeper 的读写机制:
Zookeeper是一个由多个server组成的集群:
- 一个leader,多个follower
- 每个server保存一份数据副本
- 全局数据一致
- 分布式读写
- 更新请求转发,由leader实施
Zookeeper节点数据操作流程:
配置多个实例共同构成一个集群对外提供服务以达到水平扩展的目的,每个服务器上的数据是相同的,每一个服务器均可以对外提供读和写的服务,这点和redis是相同的,即对客户端来讲每个服务器都是平等的。
客户端会和所有的节点建立链接,如果客户端正好链接的节点的角色是 leader,直接由leader处理请求;
那如果链接的节点不是 leader,是 follower,则由follower将请求转发给leader
注:1.在Client向Follwer(leader)发出一个写的请求
2.Follwer把请求发送给Leader
3.Leader接收到以后开始发起投票并通知Follwer进行投票(事务请求提议 Proposal 的投票)
4.Follwer把投票结果发送给Leader
5.Leader将结果汇总后如果需要写入,则开始写入同时把写入操作通知给Follower,然后commit;
6.Follwer把请求结果返回给Client
• Follower主要有四个功能:
• 1. 向Leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息);
• 2 .接收Leader消息并进行处理;
• 3 .接收Client的请求,如果为写请求,发送给Leader进行投票;
• 4 .返回Client结果。
• Follower的消息循环处理如下几种来自Leader的消息:
• 1 .PING消息: 心跳消息;
• 2 .PROPOSAL消息:Leader发起的提案,要求Follower投票;
• 3 .COMMIT消息:服务器端最新一次提案的信息;
• 4 .UPTODATE消息:表明同步完成;
• 5 .REVALIDATE消息:根据Leader的REVALIDATE结果,关闭待revalidate的session还是允许其接受消息;
• 6 .SYNC消息:返回SYNC结果到客户端,这个消息最初由客户端发起,用来强制得到最新的更新。
三个核心选举原则:
(1)Zookeeper集群中只有超过半数以上的服务器启动,集群才能正常工作;
(2)在集群正常工作之前,myid小的服务器给myid大的服务器投票,直到集群正常工作,选出Leader;
(3)选出Leader之后,之前的服务器状态由Looking改变为Following,以后的服务器都是Follower。
半数运行机制:
集群至少需要三台服务器,且官方文档强烈建议使用奇数个服务器,因为 zookeeper 是通过判断**大多数(超过半数)**节点的的存活来判断整个服务集群是否可用的。
比如 3 个节点,它的一半是 1.5,向上取整就是 2。挂掉了 2 个就是表示整个集群挂掉。
而用偶数 4 个的话,挂掉 2 个也表示不是大部分存活,因此也会挂掉。
所以用 4 台偶数个服务器的话 ,从使用资源上来说,并不划算。
选举机制中的概念
SID:服务器ID
比如有三台服务器,编号分别是1,2,3。
SID:服务器ID,用来唯一标识一台ZooKeeper集群中的机器,每台机器不能重复,和myid一致。
编号越大在选择算法中的权重越大。
Zxid:数据ID
服务器中存放的最大数据ID.
值越大说明数据越新,在选举算法中数据越新权重越大。
Epoch:逻辑时钟
或者叫投票的次数,同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加,然后与接收到的其它服务器返回的投票信息中的数值相比,根据不同的值做出不同的判断。
Server状态:选举状态
- LOOKING,竞选状态。
- FOLLOWING,随从状态,同步leader状态,参与投票。
- OBSERVING,观察状态,同步leader状态,不参与投票。
- LEADING,领导者状态。
Zookeeper leader 选举机制
leader的选择机制:
zookeeper提供了三种方式:
- LeaderElection
- AuthFastLeaderElection
- FastLeaderElection (最新默认)——基于fast paxos算法
默认的算法是FastLeaderElection,所以这篇主要分析它的选举机制。
选举过程(第一次启动):
下面以一个简单的例子来说明整个选举的过程:
假设有五台服务器组成的Zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的。
假设这些服务器从id1-5,依序启动:
因为一共5台服务器,只有超过半数以上,即最少启动3台服务器,集群才能正常工作。
(1)服务器1启动,发起一次选举。
服务器1投自己一票。此时服务器1票数一票,不够半数以上(3票),选举无法完成;
服务器1状态保持为LOOKING;
(2)服务器2启动,再发起一次选举。
服务器1和2分别投自己一票,此时服务器1发现服务器2的id比自己大,更改选票投给服务器2;
此时服务器1票数0票,服务器2票数2票,不够半数以上(3票),选举无法完成;
服务器1,2状态保持LOOKING;
(3)服务器3启动,发起一次选举。
与上面过程一样,服务器1和2先投自己一票,然后因为服务器3id最大,两者更改选票投给为服务器3;
此次投票结果:服务器1为0票,服务器2为0票,服务器3为3票。此时服务器3的票数已经超过半数(3票),服务器3当选Leader。
服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;
(4)服务器4启动,发起一次选举。
此时服务器1,2,3已经不是LOOKING状态,不会更改选票信息。交换选票信息结果:服务器3为3票,服务器4为1票。
此时服务器4服从多数,更改选票信息为服务器3;
服务器4并更改状态为FOLLOWING;
(5)服务器5启动,同4一样投票给3,此时服务器3一共5票,服务器5为0票;
服务器5并更改状态为FOLLOWING;
最终Leader是服务器3,状态为LEADING;
其余服务器是Follower,状态为FOLLOWING。
选举过程(非第一次启动、运行期间):
此时部分服务器初始化启动和服务器运行期间无法与Leader保持连接
当一台机器进入Leader选举流程时,当前集群也可能会处于以下两种状态:
(1)集群中本来就已经存在一个Leader
对于已经存在Leader的情况,机器试图去选举Leader时,会被告知当前服务器的Leader信息,对于该机器来说,仅仅需要和Leader机器建立连接,并进行状态同步即可。
(2)集群中确实不存在Leader
假设ZooKeeper由5台服务器组成,SID分别为1、2、3、4、5,ZXID分别为8、8、8、7、7,并且此时SID为3的服务器是Leader。某一时刻,3和5服务器出现故障,因此开始进行Leader选举。
SID为1、2、4的机器投票情况如下:
EPOCH ZXID SID
1 8 1
1 8 2
1 7 4
选举Leader的规则是:
- EPOCH最大的直接胜出
- EPOCH相同时,事务ID(ZXID)最大的胜出
- 事务ID(ZXID)相同时,服务器ID(SID)最大的胜出
6、Zookeeper同步机制
Zookeeper集群完成Leader选举后,会进行Leader和Follower的数据同步(或叫状态同步),数据同步是保证服务器数据一致,可以提供服务的前提。
选完Leader以后,zk就进入状态同步过程。(在数据完成同步之前,不可以对客户端提供服务)
1、Leader等待server连接;
2、Follower连接leader,将最大的zxid发送给leader;
3、Leader根据follower的zxid确定同步点;
4、完成同步后通知follower 已经成为uptodate状态;
5、Follower收到uptodate消息后,又可以重新接受client的请求进行服务了。
数据同步分类
Zookeeper中数据同步一共有四类,如下。
- **DIFF:直接差异化同步。**如果 Follower 的记录和 Leader 的记录相差的不多,使用增量同步的方式将一个一个写请求发送给 Follower
- TRUNC+DIFF:先回滚再差异化同步
- TRUNC:仅回滚同步。这个情况的出现代表 Follower 的 zxid 是领先于当前的 Leader 的(可能是以前的 Leader),需要 Follower 自行把多余的部分给截断,降级到和 Leader 一致
- **SNAP:全量同步。**如果 Follower 的记录和当前 Leader 相差太多,Leader 直接将自己的整个内存数据发送给 Follower
7、Zookeeper工作原理(支持选举、同步)
Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。
Zab协议有两种模式,它们分别是 恢复模式(选主)和广播模式(同步)。
-
恢复模式:当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状态同步以后,恢复 模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
-
广播模式:一旦leader已经和多数的follower进行了状态同步后,他就可以开始广播消息了,即进入广播状态。这时候当一个server加入zookeeper服务中,它会在恢复模式下启动,发现leader,并和leader进行状态同步。待到同步结束,它也参与消息广播。Zookeeper服务一直维持在Broadcast状态,直到leader崩溃了或者leader失去了大部分的followers支持。
广播模式需要保证proposal被按顺序处理,因此zk采用了**递增的事务id号(zxid)**来保证。所有的提议(proposal)都在被提出的时候加上了zxid。
8、Zookeeper节点宕机如何处理?
Zookeeper 本身也是集群,推荐配置不少于 3 个服务器。Zookeeper 自身也要保证当一个节点宕机时,其他节点会继续提供服务。
如果是一个 Follower 宕机,还有 2 台服务器提供访问,因为 Zookeeper 上的数据是有多个副本的,数据并不会丢失;
如果是一个 Leader 宕机,Zookeeper 会选举出新的 Leader。
ZK 集群的机制是只要超过半数的节点正常,集群就能正常提供服务。只有在 ZK节点挂得太多,只剩一半或不到一半节点能工作,集群才失效。
所以
3 个节点的 cluster 可以挂掉 1 个节点(leader 可以得到 2 票>1.5)
2 个节点的 cluster 就不能挂掉任何 1 个节点了(leader 可以得到 1 票<=1)
9、作为注册中心使用,zookeeper和eureka的区别
1、Zookeeper当master挂了,会在30-120s进行leader选举,这点类似于redis的哨兵机制,在选举期间Zookeeper是不可用的,这么长时间不能进行服务注册,是无法忍受的,别说30s,5s都不能忍受。这时Zookeeper集群会瘫痪,这也是Zookeeper的CP,保持节点的一致性,牺牲了A/高可用。
2、而Eureka不会,即使Eureka有部分挂掉,还有其他节点可以使用的,他们保持平级的关系,只不过信息有可能不一致,这就是AP,牺牲了C/一致性。并且Eureka还提供了自我保护机制(15分钟内超过85%的服务节点没有心跳/down),这点我觉得确实要比Zookeeper好,即使服务不可用,也会保留当前失效的微服务,默认90秒,在这90秒Eureka不会注销微服务,在这90秒内仍然可以接受新的服务注册,只是不会同步到其他节点上。当坏掉的服务恢复的时候,会自动加入到节点上,也是高可用的一种。然后退出自我保护机制,这也是应对网络异常的一种机制。---- AP更推荐
CAP:指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
- Consistent一致性: 同样数据在分布式系统中所有地方都是被复制成相同——等同于所有节点都能同步到客户端更新的最新数据
- Available可用性: 所有在分布式系统活跃的节点都能够处理操作且能响应查询——每次请求都能在规定时间内获得响应,但是不保证获取的数据为最新数据
- Partition Tolerant分区容错性: 在两个复制系统之间,如果发生了计划之外的网络连接问题,对于这种情况,有一套容错性设计来保证。——系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
注意:分布式架构的时候必须做出取舍。分区容忍性是我们必须需要实现的。所以我们只能在一致性和可用性之间进行权衡,没有NoSQL系统能同时保证这三点。多余大多数web应用,其实并不需要强一致性。因此牺牲C换取P,这是目前分布式数据库产品的方向。
10、Zookeeper部署模式
Zookeeper 的三种部署模式:
Zookeeper 有三种部署模式:
- 单机部署:一台集群上运行;
- 集群部署:多台集群运行;
- 伪集群部署:一台集群启动多个 Zookeeper 实例运行。
集群部署服务器个数
集群规则为 2N+1 (奇数)台,N>0,即 3 台。
集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。
集群是否支持动态添加机器!
其实就是水平扩容了,Zookeeper 在这方面不太好。两种方式:
全部重启:关闭所有 Zookeeper 服务,修改配置之后启动。不影响之前客户端的会话。
逐个重启:在过半存活即可用的原则下,一台机器重启不影响整个集群对外提供服务。这是比较常用的方式。
3.5 版本开始支持动态扩容。:在Zookeeper集群不停止的情况下,向这个集群中加入新的节点或移除现有的节点。这是zookeeper3.5+才有的新功能。