1.Zookeeper概述
Zookeeper是一个开源的,分布式的,为分布式应用提供协调服务的Apache顶级项目。
从设计模式上来说,zookeeper是一个基于观察者模式设计的分布式服务管理框架,负责存储和大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,zookeeper就将负责通知已在zookeeper上注册的那些观察者作出相应的反应。
简单来说:zookeeper=文件系统+通知机制
1.1.Zookeeper的特点
Zookeeper主要有以下6个特点:
(1)zookeeper是一个由1个Leader和多个Fellower和Observer组成的集群。
(2)集群中只要有半数以上的节点存活,就能正常提供服务。
(3)全局数据一致性,每个Server保存一份相同的数据副本,client无论连接到哪个Server,数据都是一致的。
(4)更新请求顺序执行,来自同一个client的更新请求按其发送顺序依次执行。
(5)数据更新原子性,一次更新要么成功要么失败。
(6)实时性,在一定时间范围内,client能读到最新数据。
1.2.Zookeeper集群的角色
Zookeeper集群中每个节点分别扮演着三种角色:
(1)Leader:一个集群只有一个Leader,它会发起并维护各Follower和Observer间的心跳。所有写操作必须通过Leader完成并广播给其他服务器。
(2)Follower:一个集群可能存在多个Follower,它会响应Leader的心跳。可直接处理客户端的读请求,但写请求需转发给Leader处理,并负责在Leader处理写请求时对请求进行投票。
(3)Observer:与Follower类似,但无投票权。
1.3.Zookeeper集群节点的状态
每个Zookeeper集群节点服务器的状态有以下四种:
(1)LOOKING:不确定Leader状态。该状态下的服务器认为集群中没有Leader,会发起Leader选举。
(2)FOLLOWING:跟随者状态。
(3)LEADING:领导者状态。
(4)OBSERVING:观察者状态。不参与选举,也不参与写操作时的投票。
1.4.应用场景
Zookeeper的主要应用场景有:统一命名服务、统一配置管理、服务器节点动态上下线、软负载均衡。
2.Zookeeper数据模型
Zookeeper中数据模型结构如下图所示:
与Unix文件系统类似,呈树状结构,每个节点称为一个ZNode。每一个ZNode默认能够存储1MB的数据,每个ZNode的路径作为唯一标识。
2.Zookeeper主要流程
Zookeeper最主要的流程就是读和写。
2.1.写Leader
整个流程如下图所示:
共分为五步:
(1)客户端向Leader发起写请求
(2)Leader将写请求以Proposal的形式发给所有Follower并等待ACK
(3)Follower收到Leader的Proposal后返回ACK
(4)Leader得到过半数的ACK(Leader对自己默认有一个ACK)后向所有的Follower和Observer发送Commit
(5)Leader将处理结果返回给客户端
在第4步中过半数表示:(1+回复ACK的Follower数)/(1+所有Follower数)>1/2。Observer不参与。
2.2.写Fellower或Observer
整个流程如下图所示:
由上图可知:
Follower/Observer均可接受写请求,但不能直接处理,而需要将写请求转发给Leader处理。
除了多了一步请求转发leader,其它流程与直接写Leader无任何区别。
2.3.读操作
整个流程如下图所示:
Leader、Follower和Observer都可直接处理读请求,从本地内存读取数据并返回给客户端即可。
3.Zookeeper选举机制
Zookeeper选举机制,就是选举Leader的机制。
zookeeper提供三种不同的选举机制:LeaderElection、AuthFastLeaderElection和FastLeaderElection。
3.1.选举相关的术语
这里有两个重要的术语:
(1)myid:每个Zookeeper服务器,都需要在数据文件夹下创建一个名为myid的文件,该文件包含整个Zookeeper集群唯一的ID(整数)。
(2)zxid:类似于RDBMS中的事务ID,用于标识一次更新操作的Proposal ID。为了保证顺序性,该zxid必须单调递增。因此Zookeeper使用一个64位的数来表示,高32位是Leader的epoch,从1开始,每次选出新的Leader,epoch加一。低32位为该epoch内的序号,每次epoch变化,都将低32位的序号重置。这样保证了zkid的全局递增性。
3.2.FastLeaderElection
FastLeaderElection是默认的选举机制。
3.2.1.投票数据结构
选举是通过投票来实现的,每个投票请求中,请求体数据结构包含以下6个字段:
(1)logicClock:每个服务器会维护一个自增的整数,名为logicClock,它表示这是该服务器发起的第多少轮投票。
(2)state:当前服务器的状态。
(3)self_id:当前服务器的myid。
(4)self_zxid:当前服务器上所保存的数据的最大zxid。
(5)vote_id:被推举的服务器的myid。
(6)vote_zxid:被推举的服务器上所保存的数据的最大zxid。
3.2.2.投票过程
投票遵循以下8个流程。
3.2.2.1.自增选举轮次
Zookeeper规定所有有效的投票都必须在同一轮次中。每个服务器在开始新一轮投票时,会先对自己维护的logicClock进行自增操作。
3.2.2.2.初始化选票
每个服务器在广播自己的选票前,会将自己的投票箱清空。该投票箱记录了所收到的选票。例:服务器2投票给服务器3,服务器3投票给服务器1,则服务器1的投票箱为(2, 3), (3, 1), (1, 1)。票箱中只会记录每一投票者的最后一票,如投票者更新自己的选票,则其它服务器收到该新选票后会在自己票箱中更新该服务器的选票。
3.2.2.3.发送初始化选票
每个服务器最开始都是通过广播把票投给自己。
3.2.2.4.接收外部投票
服务器会尝试从其它服务器获取投票,并记入自己的投票箱内。如果无法获取任何外部投票,则会确认自己是否与集群中其它服务器保持着有效连接。如果是,则再次发送自己的投票;如果否,则马上与之建立连接。
3.2.2.5.判断选举轮次
收到外部投票后,首先会根据投票信息中所包含的logicClock来进行不同处理。有以下三种情况:
(1)外部投票的logicClock大于自己的logicClock。说明该服务器的选举轮次落后于其它服务器的选举轮次,立即清空自己的投票箱并将自己的logicClock更新为收到的logicClock,然后再对比自己之前的投票与收到的投票以确定是否需要变更自己的投票,最终再次将自己的投票广播出去。
(2)外部投票的logicClock小于自己的logicClock。当前服务器直接忽略该投票,继续处理下一个投票。
(3)外部投票的logickClock与自己的相等。当时进行选票PK。
3.2.2.7.统计选票
如果已经确定有过半服务器认可了自己的投票(可能是更新后的投票),则终止投票。否则继续接收其它服务器的投票。
3.2.2.8.更新服务器状态
投票终止后,服务器开始更新自身状态。若过半的票投给了自己,则将自己的服务器状态更新为LEADING,否则将自己的状态更新为FOLLOWING
4.Zookeeper监听器原理
简单的监听过程可分为以下6个步骤。
(1)首先要有一个main线程
(2)在main线程中创建zookeeper客户端,这时就会创建两个线程,一个负责网络连接通信(connect),一个负责监听(listener)
(3)通过connect线程将注册的监听事件发送给zookeeper
(4)在zookeeper的监听器列表中将注册的监听事件添加到列表中
(5)zookeeper监听到有数据或路径变化,就会将这个消息发送给listener线程
(6)listener线程内部调用了process方法
5.Stat结构体
stat命令可查看节点的状态,stat对象有以下字段: