zookeeper
zookeeper
zookeeper
zookeeper概述
Zookeeper是一个分布式协调服务的开源框架。ZooKeeper本质上是一个分布式的小文件存储系统。提供基于类似于文件系统的目录树方式的数据存储,并且可以对树中的节点进行有效管理。从而用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。诸如:统一命名服务、分布式配置管理、分布式消息队列、分布式锁、分布式协调等功能。
zookeeper特性
1. 全局数据一致:集群中每个服务器保存一份相同的数据副本,client无论连接到哪个服务器,展示的数据都是一致的,这是最重要的特征;
2. 可靠性:如果消息被其中一台服务器接受,那么将被所有的服务器接受。
3. 顺序性:包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有Server上消息a都将在消息b前被发布;偏序是指如果一个消息b在消息a后被同一个发送者发布,a必将排在b前面。
4. 数据更新原子性:一次数据更新要么成功(半数以上节点成功),要么失败,不存在中间状态;
5. 实时性:Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。
zookeeper集群角色
Leader:zookeeper集群工作的核心。事务请求(写操作)的唯一调度和处理者,保证集群事务处理的顺序性;集群内部各个服务器的调度者。对于create,setData,delete等有写操作的请求,则需要统一转发给leader处理,leader需要决定编号,执行操作,这个过程称为一个事务。
Follower:处理客户端非事务(读操作)请求,转发事务请求给Leader;参与集群Leader选举投票。此外,针对访问量比较大的zookeeper集群,还可新增观察者角色。
Observer:观察者角色,观察zookeeper集群的最新状态变化并将这些状态同步过来,其对于非事务请求可以进行独立处理,对于事务请求,则会转发给Leader服务器进行处理。不会参与任何形式的投票只提供非事务服务,通常用于在不影响集群事务的处理能力的前提下提升集群的非事务处理能力。
zookeeper集群搭建
zookeeper集群搭建指的是zookeeper分布式模式安装。通常由2n+1台servers组成。这是因为为了保证Leader选举(基于Paxos算法的实现)能够得到多数的支持,所以zookeeper集群的数量一般为奇数。
zookeeper运行需要java环境,所以需要提前安装jdk。对于安装leader+follower模式的集群,大致过程如下:
-
配置主机名称到IP地址映射配置
-
修改ZooKeeper配置文件
-
远程复制分发安装文件
-
设置myid
-
启动ZooKeeper集群
zookeeper shell
客户端连接
运行zkCli.sh–serverip进入命令行工具。
输入help,输出zk shell提示:
shell基本操作
创建节点
create [-s] [-e] path data acl
其中,-s或-e分别指定节点特性,顺序或临时节点,若不指定,则表示持久节点;acl用来进行权限控制。
创建顺序节点:
创建临时节点:
创建永久节点:
读取节点
与读取相关的命令有ls 命令和get 命令,ls命令可以列出Zookeeper指定节点下的所有子节点,只能查看指定节点下的第一级的所有子节点;get命令可以获取Zookeeper指定节点的数据内容和属性信息。
ls path [watch]
get path [watch]
ls2 path [watch]
更新节点
set path data [version]
data就是要更新的新内容,version表示数据版本。
现在dataVersion已经变为1了,表示进行了更新。
删除节点
delete path [version]
若删除节点存在子节点,那么无法删除该节点,必须先删除该子节点,再删除父节点。
Rmr path
可以递归删除节点。
zookeeper数据模型
ZooKeeper的数据模型,在结构上和标准文件系统的非常相似,拥有一个层次的命名空间,都是采用树形层次结构,ZooKeeper树中的每个节点被称为—Znode。和文件系统的目录树一样,ZooKeeper树中的每个节点可以拥有子节点。但也有不同之处:
1. Znode兼具文件和目录两种特点。既像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分,并可以具有子Znode。用户对Znode具有增、删、改、查等操作(权限允许的情况下)。
2. Znode具有原子性操作,读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。另外,每一个节点都拥有自己的ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的操作。
3. Znode存储数据大小有限制。ZooKeeper虽然可以关联一些数据,但并没有被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,比如分布式应用中的配置文件信息、状态信息、汇集位置等等。这些数据的共同特性就是它们都是很小的数据,通常以KB为大小单位。ZooKeeper的服务器和客户端都被设计为严格检查并限制每个Znode的数据大小至多1M,当时常规使用中应该远小于此值。
4. Znode通过路径引用,如同Unix中的文件路径。路径必须是绝对的,因此他们必须由斜杠字符来开头。除此以外,他们必须是唯一的,也就是说每一个路径只有一个表示,因此这些路径不能改变。在ZooKeeper中,路径由Unicode字符串组成,并且有一些限制。字符串"/zookeeper"用以保存管理信息,比如关键配额信息。
数据结构图
图中的每个节点称为一个Znode。每个Znode由3部分组成:
①stat:此为状态信息, 描述该Znode的版本, 权限等信息
②data:与该Znode关联的数据
③children:该Znode下的子节点
节点类型
Znode有两种,分别为临时节点和永久节点。
节点的类型在创建时即被确定,并且不能改变。
临时节点:该节点的生命周期依赖于创建它们的会话。一旦会话结束,临时节点将被自动删除,当然可以也可以手动删除。临时节点不允许拥有子节点。
永久节点:该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。
Znode还有一个序列化的特性,如果创建的时候指定的话,该Znode的名字后面会自动追加一个不断增加的序列号。序列号对于此节点的父节点来说是唯一的,这样便会记录每个子节点创建的先后顺序。它的格式为"%10d"(10位数字,没有数值的数位用0补充,例如"0000000001")。
这样便会存在四种类型的Znode节点,分别对应;
PERSISTENT:永久节点
EPHEMERAL:临时节点
PERSISTENT_SEQUENTIAL:永久节点、序列化
EPHEMERAL_SEQUENTIAL:临时节点、序列化
节点属性
每个znode都包含了一系列的属性,通过命令get,可以获得节点的属性。
dataVersion:数据版本号,每次对节点进行set操作,dataVersion的值都会增加1(即使设置的是相同的数据),可有效避免了数据更新时出现的先后顺序问题。
cversion:子节点的版本号。当znode的子节点有变化时,cversion 的值就会增加1。
aclVersion:ACL的版本号。
cZxid:Znode创建的事务id。
mZxid:Znode被修改的事务id,即每次对znode的修改都会更新mZxid。
对于zk来说,每次的变化都会产生一个唯一的事务id,zxid(ZooKeeper Transaction Id)。通过zxid,可以确定更新操作的先后顺序。例如,如果zxid1小于zxid2,说明zxid1操作先于zxid2发生,zxid对于整个zk都是唯一的,即使操作的是不同的znode。
ctime:节点创建时的时间戳。
mtime:节点最新一次更新发生时的时间戳。
ephemeralOwner:如果该节点为临时节点, ephemeralOwner值表示与该节点绑定的session id. 如果不是, ephemeralOwner值为0.
在client和server通信之前,首先需要建立连接,该连接称为session。连接建立后,如果发生连接超时、授权失败,或者显式关闭连接,连接便处于CLOSED状态, 此时session结束。
zookeeper Watcher
ZooKeeper提供了分布式数据发布/订阅功能,一个典型的发布/订阅模型系统定义了一种一对多的订阅关系,能让多个订阅者同时监听某一个主题对象,当这个主题对象自身状态变化时,会通知所有订阅者,使他们能够做出相应的处理。
ZooKeeper中,引入了Watcher机制来实现这种分布式的通知功能。ZooKeeper允许客户端向服务端注册一个Watcher监听,当服务端的一些事件触发了这个Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能。
触发事件种类很多,如:节点创建,节点删除,节点改变,子节点改变等。
总的来说可以概括Watcher为以下三个过程:客户端向服务端注册Watcher、服务端事件发生触发Watcher、客户端回调Watcher得到触发事件情况
Watch机制特点
一次性触发:事件发生触发监听,一个watcher event就会被发送到设置监听的客户端,这种效果是一次性的,后续再次发生同样的事件,不会再次触发。
事件封装:ZooKeeper使用WatchedEvent对象来封装服务端事件并传递。WatchedEvent包含了每一个事件的三个基本属性:通知状态(keeperState),事件类型(EventType)和节点路径(path)
event异步发送:watcher的通知事件从服务端发送到客户端是异步的。
先注册再触发:Zookeeper中的watch机制,必须客户端先去服务端注册监听,这样事件发送才会触发监听,通知给客户端。
常见的通知状态和事件类型
其中连接状态事件(type=None,path=null)不需要客户端注册,客户端只需要直接处理就行了。
shell客户端设置watcher
设置节点数据变动监听
通过另一个客户端更改节点数据:
此时设置监听的节点接收通知:
zookeeper选举机制
zookeeper默认的算法是FastLeaderElection,采用投票数大于半数则胜出的逻辑。
概念
服务器ID(serverid)
比如有三台服务器,编号分别是1,2,3。
编号越大在选择算法中的权重越大。
选举状态(server状态)
LOOKING,竞选状态。
FOLLOWING,随从状态,同步leader状态,参与投票。
OBSERVING,观察状态,同步leader状态,不参与投票。
LEADINFG,领导者状态。
数据ID(zxid)
服务器中存放的最新数据version
值越大说明数据越新,在选举算法中数据越新权重越大。
逻辑时钟(epoch)
也叫投票的次数,同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加,然后与接收到的其他服务器返回的投票信息中的数值相比,根据不同的值做出不同的判断。
全新集群选举
初始化启动期间Leader选举流程如下图所示:
在集群初始化阶段,当有一台服务器ZK1启动时,其单独无法进行和完成Leader选举,当第二台服务器ZK2启动时,此时两台机器可以相互通信,每台机器都试图找到Leader,于是进入Leader选举过程。选举过程开始,过程如下:
(1) 每个Server发出一个投票。由于是初始情况,ZK1和ZK2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举的服务器的myid和ZXID,使用(myid, ZXID)来表示,此时ZK1的投票为(1, 0),ZK2的投票为(2, 0),然后各自将这个投票发给集群中其他机器。
(2) 接受来自各个服务器的投票。集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自LOOKING状态的服务器。
(3) 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行比较,规则如下
- 优先检查ZXID。ZXID比较大的服务器优先作为Leader。
- 如果ZXID相同,那么就比较myid。myid较大的服务器作为Leader服务器。
对于ZK1而言,它的投票是(1, 0),接收ZK2的投票为(2, 0),首先会比较两者的ZXID,均为0,再比较myid,此时ZK2的myid最大,于是ZK2胜。ZK1更新自己的投票为(2, 0),并将投票重新发送给ZK2。
(4) 统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于ZK1、ZK2而言,都统计出集群中已经有两台机器接受了(2, 0)的投票信息,此时便认为已经选出ZK2作为Leader。
(5) 改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING。当新的Zookeeper节点ZK3启动时,发现已经有Leader了,不再选举,直接将直接的状态从LOOKING改为FOLLOWING。
非全新集群选举
在Zookeeper运行期间,如果Leader节点挂了,那么整个Zookeeper集群将暂停对外服务,进入新一轮Leader选举。
假设正在运行的有ZK1、ZK2、ZK3三台服务器,当前Leader是ZK2,若某一时刻Leader挂了,此时便开始Leader选举。选举过程如下图所示。
(1) 变更状态。Leader挂后,余下的非Observer服务器都会讲自己的服务器状态变更为LOOKING,然后开始进入Leader选举过程。
(2) 每个Server会发出一个投票。在运行期间,每个服务器上的ZXID可能不同,此时假定ZK1的ZXID为124,ZK3的ZXID为123;在第一轮投票中,ZK1和ZK3都会投自己,产生投票(1, 124),(3, 123),然后各自将投票发送给集群中所有机器。
(3) 接收来自各个服务器的投票。与启动时过程相同。
(4) 处理投票。与启动时过程相同,由于ZK1事务ID大,ZK1将会成为Leader。
(5) 统计投票。与启动时过程相同。
(6) 改变服务器的状态。与启动时过程相同。
CAP定理和BASE理论
在分布式系统中,为了保证数据的高可用,通常会将数据保留多个副本(replica),这些副本会放置在不同的物理机器上。
什么是数据一致性
在数据有多个副本的情况下,如果网络、服务器或者软件出现故障,会导致部分副本写入成功,部分副本写入失败。这就造成各个副本之间的数据不一致,数据内部冲突。造成事实上的数据不一致。
CAP定理
CAP理论认为在分布式的环境下设计和部署系统时,有3个核心的需求:
Consistency、Availability和Partition Tolerance,即CAP
Consistency
一致性,这个和数据库ACID的一致性类似,但这里关注的所有数据节点上的数据一致性和正确性,而数据库的ACID关注的是在一个事务内,对数据的一些约束。系统在执行过某项操作后仍然处于一致的状态。在分布式系统中,更新操作执行成功后所有的用户都应该读取到最新值。
Availability
可用性,每一个操作总是能够在一定时间内返回结果。需要注意"一定时间"和"返回结果"。"一定时间"是指,系统结果必须在给定时间内返回。"返回结果"是指系统返回操作成功或失败的结果。
Partition Tolerance
分区容忍性,是否可以对数据进行分区。这是考虑到性能和可伸缩性。
分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容忍性(P:partition Tolerance),最多只能同时满足其中两项。
权衡
在分布式系统中,分区容忍性必不可少,因为需要总是假设网络是不可靠的。因此,CAP理论实际上是要在可用性和一致性之间做权衡。
可用性和一致性往往是冲突的,很难使它们同时满足。在多个节点之间进行数据同步时,
- 为了保证一致性(CP),不能访问未同步的节点,也就失去了部分可用性。
- 为了保证可用性(AP),允许读取所有节点的数据,但是数据可能不一致。
数据一致性模型
一些分布式系统通过复制数据来提高系统的可靠性和容错性,并且将数据的不同的副本存放在不同的机器。
强一致性:
当更新操作完成之后,任何多个后续进程或者线程的访问都会返回最新的更新过的值。这种是对用户最友好的,就是用户上一次写什么,下一次就保证能读到什么。根据 CAP 理论,这种实现需要牺牲可用性。
弱一致性:
系统并不保证续进程或者线程的访问都会返回最新的更新过的值。用户读到某一操作对系统特定数据的更新需要一段时间,我们称这段时间为"不一致性窗口。系统在数据写入成功之后,不承诺立即可以读到最新写入的值,也不会具体的承诺多久之后可以读到。
最终一致性:
最终一致性强调的是系统所有的数据副本,在经过一段时间的同步后,最终达到一致的状态。
BASE理论
BASE是基本可用(Basically Available)、软状态(Soft State)和最终一致性(Eventually Consistent)三个短语的缩写。
BASE理论是对CAP中一致性和可用性权衡的结果,它的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。
基本可用
指分布式系统在出现故障的时候,保证核心可用,允许损失部分可用性。例如,电商在做促销时,为了保证购物系统的稳定性,部分消费者可能会被引导到一个降级的页面。
软状态
允许系统中的数据存在中间状态,并认为该中间状态,并认为该中间状态不会影响系统整体可用性,即允许系统不同节点的数据副本之间进行同步的过程存在时延。
最终一致性:
最终一致性强调的是系统所有的数据副本,在经过一段时间的同步后,最终达到一致的状态。
ACID要求强一致性,通常运用在传统的数据库系统上。而BASE要求最终一致性,通过牺牲一致性来达到可用性,通常运用在大型分布式系统中。
在实际的分布式场景中,不同业务单元和组件对一致性的要求是不同的,因此ACID和BASE往往会结合在一起使用。
两阶段提交
两阶段提交协议是协调所有分布式原子事务参与者,并决定提交或取消(回滚)的分布式算法。
协议参与者
在两阶段提交协议中,系统一般包括两类机器(或节点):一类为协调者(coordinator),通常系统只有一个;另一类为事务参与者(participants,cohorts或workers),一般包括多个,在数据存储系统中可以理解为数据副本的个数。协议中假设每个节点都会记录写前日志(write-ahead log)并持久性存储,即使节点发生故障日志也不会丢失。协议中同时假设节点不会发生永久性故障而且任意两个节点都可以互相通信。
两个阶段的执行
1.请求阶段(commit-request phase,或称表决阶段,voting phase)
在请求阶段,协调者将通知事务参与者准备提交或取消事务,然后进入表决过程。
在表决过程中,参与者将告知协调者自己的决策:同意(事务参与者本地作业执行成功)或取消(本地作业执行故障)。
2.提交阶段(commit phase)
在该阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消。
当且仅当所有的参与者同意提交事务协调者才通知所有的参与者提交事务,否则协调者将通知所有的参与者取消事务。
参与者在接收到协调者发来的消息后将执行响应的操作。
两个阶段提交的缺点
1.同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的。
当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。
2.单点故障。由于协调者的重要性,一旦协调者发生故障。
参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)
3.数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。
而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。
zookeeper一致性原理
概述
zookeeper的核心是原子广播机制,这个机制保证了各个server之间的同步。实现这个机制的协议叫做ZAB(zookeeper atomic broadcast)协议。zookeeper是通过zab协议来保证分布式事务的最终一致性。
恢复模式
当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数server完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和server具有相同的系统状态。
广播模式
一旦Leader已经和多数的Follower进行了状态同步后,他就可以开始广播消息了,即进入广播状态。这时候当一个Server加入ZooKeeper服务中,它会在恢复模式下启动,发现Leader,并和Leader进行状态同步。待到同步结束,它也参与消息广播。ZooKeeper服务一直维持在Broadcast状态,直到Leader崩溃了或者Leader失去了大部分的Followers支持。
Broadcast模式极其类似于分布式事务中的2pc(two-phrase commit 两阶段提交):即Leader提起一个决议,由Followers进行投票,Leader对投票结果进行计算决定是否通过该决议,如果通过执行该决议(事务),否则什么也不做。
在广播模式ZooKeeper Server会接受Client请求,所有的写请求都被转发给领导者,再由领导者将更新广播给跟随者。当半数以上的跟随者已经将修改持久化之后,领导者才会提交这个更新,然后客户端才会收到一个更新成功的响应。这个用来达成共识的协议被设计成具有原子性,因此每个修改要么成功要么失败。
zab协议详解
广播模式
广播模式类似一个简单的两阶段提交:Leader发起一个请求,收集选票,并且最终提交,图3.3演示了我们协议的消息流程。我们可以简化该两阶段提交协议,因为我们并没有"aborts"的情况。followers要么确认Leader的Propose,要么丢弃该Leader的Propose。没有"aborts"意味着,只要有指定数量的机器确认了该Propose,而不是等待所有机器的回应。
广播协议在所有的通讯过程中使用TCP的FIFO信道,通过使用该信道,使保持有序性变得非常的容易。通过FIFO信道,消息被有序的deliver。只要收到的消息一被处理,其顺序就会被保存下来。
Leader会广播已经被deliver的Proposal消息。在发出一个Proposal消息前,Leader会分配给Proposal一个单调递增的唯一id,称之为zxid。因为Zab保证了因果有序, 所以递交的消息也会按照zxid进行排序。广播是把Proposal封装到消息当中,并添加到指向Follower的输出队列中,通过FIFO信道发送到 Follower。当Follower收到一个Proposal时,会将其写入到磁盘,可以的话进行批量写入。一旦被写入到磁盘媒介当 中,Follower就会发送一个ACK给Leader。 当Leader收到了指定数量的ACK时,Leader将广播commit消息并在本地deliver该消息。当收到Leader发来commit消息 时,Follower也会递交该消息。
需要注意的是, 该简化的两阶段提交自身并不能解决Leader故障,所以我们 添加恢复模式来解决Leader故障。
恢复模式
正常工作时Zab协议会一直处于广播模式,直到Leader故障或失去了指定数量的Followers。 为了保证进度,恢复过程中必须选举出一个新Leader,并且最终让所有的Server拥有一个正确的状态。对于Leader选举,需要一个能够成功高几 率的保证存活的算法。Leader选举协议,不仅能够让一个Leader得知它是leader,并且有指定数量的Follower同意该决定。如果 Leader选举阶段发生错误,那么Servers将不会取得进展。最终会发生超时,重新进行Leader选举。在我们的实现中,Leader选举有两种不同的实现方式。如果有指定数量的Server正常运行,快速选举的完成只需要几百毫秒。
zookeeper应用场景
Zookeeper Java API
org.apache.zookeeper.Zookeeper
Zookeeper 是在Java中客户端主类,负责建立与zookeeper集群的会话,并提供方法进行操作。
org.apache.zookeeper.Watcher
Watcher接口表示一个标准的事件处理器,其定义了事件通知相关的逻辑,包含KeeperState和EventType两个枚举类,分别代表了通知状态和事件类型,同时定义了事件的回调方法:process(WatchedEvent event)。
process方法是Watcher接口中的一个回调方法,当ZooKeeper向客户端发送一个Watcher事件通知时,客户端就会对相应的process方法进行回调,从而实现对事件的处理。
基本使用
建立javamaven项目,引入mavenpom坐标。
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
更多操作示例
数据发布与订阅(配置中心)
发布与订阅模型,即所谓的配置中心,顾名思义就是发布者将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新。
应用在启动的时候会主动来获取一次配置,同时,在节点上注册一个Watcher,这样一来,以后每次配置有更新的时候,都会实时通知到订阅的客户端,从来达到获取最新配置信息的目的。比如:
分布式搜索服务中,索引的元信息和服务器集群机器的节点状态存放在ZK的一些指定节点,供各个客户端订阅使用。
注意:适合数据量很小的场景,这样数据更新可能会比较快。
命名服务(Naming Service)
在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。被命名的实体通常可以是集群中的机器,提供的服务地址,远程对象等等——这些我们都可以统称他们为名字(Name)。其中较为常见的就是一些分布式服务框架中的服务地址列表。通过调用ZK提供的创建节点的API,能够很容易创建一个全局唯一的path,这个path就可以作为一个名称。
阿里巴巴集团开源的分布式服务框架Dubbo中使用ZooKeeper来作为其命名服务,维护全局的服务地址列表。
Zookeeper分布式锁
有了zookeeper的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一个是保持独占,另一个是控制时序。
对于第一类,我们将zookeeper上的一个znode看作是一把锁,通过createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的distribute_lock 节点就释放出锁。
对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便。
获取分布式锁的流程
在获取分布式锁的时候在locker节点下创建临时顺序节点,释放锁的时候删除该临时节点。客户端调用createNode方法在locker下创建临时顺序节点, 然后调用getChildren("locker")来获取locker下面的所有子节点,注意此时不用设置任何Watcher。客户端获取到所有的子节点path之后,如果发现自己创建的节点在所有创建的子节点序号最小,那么就认为该客户端获取到了锁。如果发现自己创建的节点并非locker所有子节点中最小的,说明自己还没有获取到锁,此时客户端需要找到比自己小的那个节点,然后对其调用exist()方法,同时对其注册事件监听器。之后,让这个被关注的节点删除,则客户端的Watcher会收到相应通知,此时再次判断自己创建的节点是否是locker子节点中序号最小的,如果是则获取到了锁,如果不是则重复以上步骤继续获取到比自己小的一个节点并注册监听。当前这个过程中还需要许多的逻辑判断。