Zookeeper
1. Zookeeper ZAB协议
为了保证写操作的一致性和可用性,Zookeeper专门设计了一种名为原子广播(ZAB)的支持崩溃恢复的一致性协议。基于该协议,Zookeeper实现了一种主从模式的系统架构来保持集群中各个副本之间的数据一致性。
ZAB(ZooKeeper Atomic Broadcast 原子广播) 协议是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。 在 ZooKeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性,基于该协议,ZooKeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。
ZAB协议包括两种基本的模式,分别是崩溃恢复和消息广播。当整个服务框架在启动过程中,或是当Leader
服务器出现网络中断、崩溃退出与重启等异常情况时,ZAB 协议就会进入恢复模式并选举产生新的Leader
服务器。当选举产生了新的Leader
服务器,同时集群中已经有过半的机器与该Leader
服务器完成了状态同步之后,ZAB协议就会退出恢复模式。其中,所谓的状态同步是指数据同步,用来保证集群中存在过半的机器能够和Leader
服务器的数据状态保持一致。
当集群中已经有过半的Follower
服务器完成了和Leader
服务器的状态同步,那么整个服务框架就可以进入了消息广播模式了。当一台同样遵守ZAB协议的服务器启动后加入到集群中时,如果此时集群中已经存在一个Leader
服务器在负责进行消息广播,那么新加入的服务器就会自觉地进入数据恢复模式:找到Leader
所在的服务器,并与其进行数据同步,然后一起参与到消息广播流程中去。正如上文介绍中所说的,ZooKeeper设计成只允许唯一的一个Leader
服务器来进行事务请求的处理。Leader
服务器在接收到客户端的事务请求后,会生成对应的事务提案并发起一轮广播协议;而如果集群中的其他机器接收到客户端的事务请求,那么这些非Leader
服务器会首先将这个事务请求转发给Leader
服务器。
根据ZAB协议,所有的写操作都必须是通过Leader
完成,Leader
写入本地日志后再复制到所有的Follower
节点。
一旦Leader
节点无法工作,ZAB协议能够自动从Follower
节点中重新选出一个合适的替代者,即新的Leader
,该过程即为领导选举。该领导选举过程,是ZAB协议中最为重要和复杂的过程。
2. Zookeeper读写流程
写Leader
通过Leader
进行写数据的流程如下:
由上图可见,通过Leader
进行写操作,主要分为五步:
- 客户端向
Leader
发起写请求 Leader
将写请求以Proposal
(提案)的形式发给所有Follower
并等待ACK
Follower
收到Leader
的Proposal
后返回ACK
Leader
得到过半数的ACK
(Leader
对自己默认有一个ACK)后向所有的Follower
和Observer
发送Commmit
Leader
将处理结果返回给客户端
需要注意的是:
Leader
并不需要得到Observer
的ACK
,因为Obsever
并没有投票权Leader
不需要得到所有Follower
的ACK
,只要收到过半的ACK
即可,同时Leader
本身对自己有一个ACK
。Observer
虽然无投票权,但仍须同步Leader
的数据从而在处理读请求时可以返回尽可能新的数据。
写Follower/Obsever
通过Follower/Obsever
进行写操作的流程如下:
从上图可见,Follower/Observer
均可以接受写请求,但不能直接处理,而需要将写请求转发给Leader
处理。
读操作
Leader/Follower/Observer
都可直接处理读请求,从本地内存中读取数据并返回给客户端即可。
由于处理读请求不需要服务器之间的交互,Follower/Observer
越多,整体可处理的读请求量越大,也即读性能越好。
3. 领导选举算法
Zookeeper使用基于TCP的FastLeaderElection算法进行领导选举。原理:
1. myid
每个ZooKeeper服务器,都需要在数据文件夹下创建一个名为myid
的文件,该文件包含整个ZooKeeper集群唯一的ID(整数)。例如,某ZooKeeper集群包含三台服务器,hostname
分别为zoo1、zoo2和zoo3,其myid
分别为1、2和3,则在配置文件中其ID与hostname
必须一一对应,如下所示。在该配置文件中,server.
后面的数据即为myid
server.1=zoo1:2888:3888
server.2=zoo2:2888:3888
server.3=zoo3:2888:3888
2. zxid
类似于RDBMS中的事务ID,用于标识一次更新操作的Proposal ID
。为了保证顺序性,该zkid
必须单调递增。因此ZooKeeper使用一个64位的数来表示,高32位是Leader
的epoch
,从1开始,每次选出新的Leader
,epoch
加一。低32位为该epoch
内的序号,每次epoch
变化,都将低32位的序号重置。这样保证了zkid
的全局递增性。
3. 服务器状态
LOOKING
:不确定Leader
状态。该状态下的服务器认为当前集群中没有Leader
,会发起Leader
选举。FOLLOWING
:跟随者状态。表明当前服务器角色是Follower
,并且它知道Leader
是谁。LEADING
:领导者状态。表明当前服务器角色是Leader
,它会维护与Follower
间的心跳。OBSERVING
:观察者状态。表明当前服务器角色是Observer
,与Folower
唯一的不同在于不参与选举,也不参与集群写操作时的投票。
4. 选票的数据结构
每个服务器在进行领导选举时,会发送如下关键信息:
loginClock
:每个服务器会维护一个自增的整数,名为loginClock
,表示这是该服务器发起的第多少轮投票。state
:当前服务器的状态self_id
:当前服务器的myid
self_zxid
:当前服务器上所保存的数据的最大zxid
vote_id
:被推举的服务器的myid
vote_zxid
:被推举的服务器上所保存的数据的最大zxid
5. 投票流程
自增选举轮次
Zookeeper规定所有有效的投票都必须在同一轮次中。每个服务器在开始新一轮投票时,会先对自己维护的loginClock
进行自增操作。
初始化选票
每个服务器在广播自己的选票前,会将自己的投票箱清空。该投票箱记录了所收到的选票。例:服务器2投票给服务器3,服务器3投票给服务器1,则服务器1的投票箱为(2, 3), (3, 1), (1, 1)。票箱中只会记录每一投票者的最后一票,如投票者更新自己的选票,则其它服务器收到该新选票后会在自己票箱中更新该服务器的选票。
发送初始化选票
每个服务器最开始都是通过广播把票投给自己
接收外部投票
服务器会尝试从其它服务器获取投票,并记入自己的投票箱内。如果无法获取任何外部投票,则会确认自己是否与集群中其它服务器保持着有效连接。如果是,则再次发送自己的投票;如果否,则马上与之建立连接。
判断选举轮次
收到外部投票后,首先会根据投票信息中所包含的logicClock
来进行不同处理:
- 外部投票的
logicClock
大于自己的logicClock
。说明该服务器的选举轮次落后于其它服务器的选举轮次,立即清空自己的投票箱并将自己的logicClock
更新为收到的logicClock
,然后再对比自己之前的投票与收到的投票以确定是否需要变更自己的投票,最终再次将自己的投票广播出去。 - 外部投票的
logicClock
小于自己的logicClock
。当前服务器直接忽略该投票,继续处理下一个投票。 - 外部投票的
logickClock
与自己的相等,进行选票PK。
选票PK
选票PK时是基于(self_id,self_zxid)
与(vote_id,vote_zxid)
的对比:
- 外部投票的
logicClock
大于自己的logicClock
,则将自己的logicClock
及自己的选票的logicClock
变更为收到的logicClock
- 若
logicClock
一致,则对比二者的vote_zxid
,若外部投票的vote_zxid
比较大,则将自己的票中的vote_zxid
与vote_myid
更新为收到的票中的vote_zxid
与vote_myid
并广播出去,另外将收到的票及自己更新后的票放入自己的票箱。如果票箱内已存在(self_myid, self_zxid)
相同的选票,则直接覆盖 - 若二者
vote_zxid
一致,则比较二者的vote_myid
,若外部投票的vote_myid
比较大,则将自己的票中的vote_myid
更新为收到的票中的vote_myid
并广播出去,另外将收到的票及自己更新后的票放入自己的票箱。
统计选票
如果已经确定有过半服务器认可了自己的投票(可能是更新后的投票),则终止投票。否则继续接收其他服务器的投票。
更新服务器状态
投票终止后,服务器开始更新自身状态。若过半的票投给了自己,则将自己的服务器状态更新为LEADING
,否则将自己的状态更新为FOLLOWING
。
4. 集群启动领导选举
1. 初始投票给自己
集群刚启动时,所有服务器的logicClock
都为1,zxid
都为0。各服务器初始化后,都投票给自己,并将自己的一票存入自己的票箱,如下图所示。
在上图中,(1, 1, 0)
第一位数代表投出该选票的服务器的logicClock
,第二位数代表被推荐的服务器的myid
,第三位代表被推荐的服务器的最大的zxid
。由于该步骤中所有选票都投给自己,所以第二位的myid
即是自己的myid
,第三位的zxid
即是自己的zxid
。
此时各自的票箱中只有投给自己的一票
2. 更新投票
服务器收到外部投票后,进行选票PK,相应更新自己的选票并广播出去,并将合适的选票存入自己的票箱,如下图所示。
服务器1收到服务器2的选票(1, 2, 0)
和服务器3的选票(1, 3, 0)
后,由于所有的logicClock
都相等,所有的zxid
都相等,因此根据myid
判断应该将自己的选票按照服务器3的选票更新为(1, 3, 0)
,并将自己的票箱全部清空,再将服务器3的选票与自己的选票存入自己的票箱,接着将自己更新后的选票广播出去。此时服务器1票箱内的选票为(1, 3)
,(3, 3)
。
同理,服务器2收到服务器3的选票后也将自己的选票更新为(1, 3, 0)
并存入票箱然后广播。此时服务器2票箱内的选票为(2, 3)
,(3, ,3)
。
服务器3根据上述规则,无须更新选票,自身的票箱内选票仍为(3, 3)
。
服务器1与服务器2更新后的选票广播出去后,由于三个服务器最新选票都相同,最后三者的票箱内都包含三张投给服务器3的选票。
3. 根据选票确定角色
根据上述选票,三个服务器一致认为此时服务器3应该是Leader
。因此服务器1和2都进入FOLLOWING
状态,而服务器3进入LEADING
状态。之后Leader
发起并维护与Follower
间的心跳。
5. Follower/Leader重启选举
Follower重启选举
1. Follower重启投票给自己
Follower
重启,或者发生网络分区后找不到Leader
,会进入LOOKING
状态并发起新的一轮投票。
2. 发现已有Leader后成为Follower
服务器3收到服务器1的投票后,将自己的状态LEADING
以及选票返回给服务器1。服务器2收到服务器1的投票后,将自己的状态FOLLOWING
及选票返回给服务器1。此时服务器1知道服务器3是Leader
,并且通过服务器2与服务器3的选票可以确定服务器3确实得到了超过半数的选票。因此服务器1进入FOLLOWING
状态。
Leader重启选举
1. Follower发起新投票
Leader
(服务器3)宕机后,Follower
(服务器1和2)发现Leader
不工作了,因此进入LOOKING
状态并发起新的一轮投票,并且都将票投给自己。
2. 广播更新投票
服务器1和2根据外部投票确定是否要更新自身的选票。这里有两种情况:
- 服务器1和2的
zxid
相同。例如在服务器3宕机前服务器1与2完全与之同步。此时选票的更新主要取决于myid
的大小 - 服务器1和2的
zxid
不同。在旧Leader
宕机之前,其所主导的写操作,只需过半服务器确认即可,而不需所有服务器确认。换句话说,服务器1和2可能一个与旧Leader
同步(即zxid
与之相同)另一个不同步(即zxid
比之小)。此时选票的更新主要取决于谁的zxid
较大。
在上图中,服务器1的zxid
为11,而服务器2的zxid
为10,因此服务器2将自身选票更新为(3, 1, 11)
,如下图所示。
3. 选出新的Leader
经过上一步选票更新后,服务器1与服务器2均将选票投给服务器1,因此服务器2成为Follower
,而服务器1成为新的Leader
并维护与服务器2的心跳。
4. 旧Leader恢复后发起选举
旧的Leader
恢复后,进入LOOKING
状态并发起新一轮领导选举,并将选票投给自己。此时服务器1会将自己的LEADING
状态及选票(3, 1, 11)
返回给服务器3,而服务器2将自己的FOLLOWING
状态及选票(3, 1, 11)
返回给服务器3。如下图所示:
5. 旧Leader成为Follower
服务器3了解到Leader
为服务器1,且根据选票了解到服务器1确实得到过半服务器的选票,因此自己进入FOLLOWING
状态。
6. Zookeeper如何保证commit过的数据不丢失
1. Failover前状态
如下图共有5个节点:A作为Leader
,共收到P1、P2、P3三条消息,并且Commit
了1和2,且总体顺序为P1、P2、C1、P3、C2。根据顺序性原则,其它Follower
收到的消息的顺序肯定与之相同。其中B与A完全同步,C收到P1、P2、C1,D收到P1、P2,E收到P1,如下图所示。
这里要注意:
- 由于A没有C3,意味着收到P3的服务器的总个数不会超过一半,也即包含A在内最多只有两台服务器收到P3。在这里A和B收到P3,其它服务器均未收到P3
- 由于A已写入C1、C2,说明它已经Commit了P1、P2,因此整个集群有超过一半的服务器,即最少三个服务器收到P1、P2。在这里所有服务器都收到了P1,除E外其它服务器也都收到了P2
2. 选出Leader
旧Leader
也即A宕机后,其它服务器根据上述FastLeaderElection
算法选出B作为新的Leader
。C、D和E成为Follower
且以B为Leader
后,会主动将自己最大的zxid
发送给B,B会将Follower
的zxid
与自身zxid
间的所有被Commit
过的消息同步给Follower
,如下图所示。
在上图中:
- P1和P2都被A Commit,因此B会通过同步保证P1、P2、C1与C2都存在于C、D和E中
- P3由于未被A Commit,同时幸存的所有服务器中P3未存在于大多数据服务器中,因此它不会被同步到其它
Follower
3. 通知Follower可对外服务
同步完数据后,B会向D、C和E发送NEWLEADER命令并等待大多数服务器的ACK(下图中D和E已返回ACK,加上B自身,已经占集群的大多数),然后向所有服务器广播UPTODATE
命令。收到该命令后的服务器即可对外提供服务。
未Commit过的消息对客户端不可见
在上例中,P3未被A Commit过,同时因为没有过半的服务器收到P3,因此B也未Commit P3(如果有过半服务器收到P3,即使A未Commit P3,B会主动Commit P3,即C3),所以它不会将P3广播出去。
具体做法是,B在成为Leader
后,先判断自身未Commit的消息(本例中即P3)是否存在于大多数服务器中从而决定是否要将其Commit。然后B可得出自身所包含的被Commit过的消息中的最小zxid
(记为min_zxid
)与最大zxid
(记为max_zxid
)。C、D和E向B发送自身Commit过的最大消息zxid
(记为max_zxid
)以及未被Commit过的所有消息(记为zxid_set
)。B根据这些信息作出如下操作:
- 如果
Follower
的max_zxid
与Leader
的max_zxid
相等,说明该Follower
与Leader
完全同步,无须同步任何数据 - 如果
Follower
的max_zxid
在Leader
的(min_zxid
,max_zxid
)范围外,Leader
会通过TRUNC
命令通知Follower
将其zxid_set
中大于Follower
的max_zxid
(如果有)的所有消息全部删除
上述操作保证了未被Commit过的消息不会被Commit从而对外不可见。
上述例子中Follower上并不存在未被Commit的消息。但可考虑这种情况,如果将上述例子中的服务器数量从五增加到七,服务器F包含P1、P2、C1、P3,服务器G包含P1、P2。此时服务器F、A和B都包含P3,但是因为票数未过半,因此B作为Leader
不会Commit P3,而会通过TRUNC
命令通知F删除P3。如下图所示。
7. Zookeeper节点类型
Zookeeper提供了类似于Linux文件系统的树形结构,该树形结构中每个节点都被称为znode
,可被分为以下两类:
- Persist和Ephemeral
Persist
节点一旦被创建,便不会意外丢失,即使服务器全部重启也依然存在。在每个Persist
节点即可包含数据,也可包含子节点。Ephemeral
节点,在创建它的客户端与服务器间的Session
结束时自动被删除。服务器重启会导致Session
结束,因此Ephemeral
类型的znode
此时也会自动删除。
- Sequence和Non-sequence
Non-sequence
节点,多个客户端同时创建同一Non-sequence
节点时,只有一个可创建成功,其它匀失败。并且创建出的节点名称与创建时指定的节点名完全一样Sequence
节点,创建出的节点名在指定的名称之后带有10位10进制数的序号。多个客户端创建同一名称的节点时,都能创建成功,只是序号不同
8. Zookeeper如何进行语义保证?
ZooKeeper简单高效,同时提供如下语义保证,从而使得可以利用这些特性提供复杂的服务。
- 顺序性:客户端发起的更新会按发送顺序被应用到 ZooKeeper 上
- 原子性:更新操作要么成功要么失败,不会出现中间状态
- 单一系统镜像:一个客户端无论连接到哪一个服务器都能看到完全一样的系统镜像(即完全一样的树形结构)。根据
FastLeaderElection
机制的 ZAB 协议,写操作并不保证更新被所有的Follower
立即确认,因此通过部分Follower
读取数据并不能保证读到最新的数据,而部分Follwer
及Leader
可读到最新数据。如果一定要保证单一系统镜像,可在读操作前使用sync
方法。 - 可靠性:一个更新操作一旦被接受即不会意外丢失,除非被其它更新操作覆盖
- 最终一致性:写操作最终(而非立即)会对客户端可见
9. Zookeeper Watch机制
所有对 ZooKeeper 的读操作,都可附带一个Watch
。一旦相应的数据有变化,该Watch
即被触发。
原理如下:
- 首先有一个
main()
线程 - 在
main()
线程中创建Zookeeper客户端,这时就会创建两个线程,一个负责网络连接通信(connect
),一个负责监听(listener
) - 通过
connect
线程将注册的监听事件发送给Zookeeper - 在Zookeeper的注册监听器列表中将注册的监听事件添加到列表中
- Zookeeper监听到有数据或者路径变化,就会将这个消息发送给
listener
线程 listener
线程内部调用了process()
方法
Watch
有如下特点:
- 主动推送:
Watch
被触发时,由 ZooKeeper 服务器主动将更新推送给客户端,而不需要客户端轮询。 - 一次性:数据变化时,
Watch
只会被触发一次。如果客户端想得到后续更新的通知,必须要在Watch
被触发后重新注册一个 Watch。 - 可见性:如果一个客户端在读请求中附带
Watch
,Watch
被触发的同时再次读取数据,客户端在得到Watch
消息之前肯定不可能看到更新后的数据。换句话说,更新通知先于更新结果。 - 顺序性:如果多个更新触发了多个
Watch
,那Watch
被触发的顺序与更新顺序一致。
10. Zookeeper实现分布式锁原理
分布式锁需要实现的关键点:
最多一个获取锁/称为Leader:对于分布式锁(这里特指排它锁)而言,任意时刻,最多只有一个进程(对于单进程内的锁而言是单线程)可以获得锁。
这里的
Leader
指的是zookeeper的客户端,而不是服务器端锁重入/确认自己是Leader:对于分布式锁,需要保证获得锁的进程在释放锁之前可再次获得锁,即锁的可重入性;对于领导选举,
Leader
需要能够确认自己已经获得领导权,即确认自己是Leader
。释放锁/放弃领导权:锁的获得者应该能够正确释放已经获得的锁,并且当获得锁的进程宕机时,锁应该自动释放,从而使得其它竞争方可以获得该锁,从而避免出现死锁的状态。领导应该可以主动放弃领导权,并且当领导所在进程宕机时,领导权应该自动释放,从而使得其它参与者可重新竞争领导而避免进入无主状态。
感知锁释放 / 领导权的放弃:当获得锁的一方释放锁时,其它对于锁的竞争方需要能够感知到锁的释放,并再次尝试获取锁。原来的
Leader
放弃领导权时,其它参与方应该能够感知该事件,并重新发起选举流程。
非公平领导选举实现分布式锁
1. 选主过程:
假设有三个ZooKeeper的客户端,如下图所示,同时竞争Leader
。这三个客户端同时向ZooKeeper集群注册Ephemeral
且Non-sequence
类型的节点,路径都为/zkroot/leader
(工程实践中,路径名可自定义)。
如上图所示,由于是Non-sequence
节点,这三个客户端只会有一个创建成功,其它节点均创建失败。此时,创建成功的客户端(即上图中的Client 1)即成功竞选为Leader
。其它客户端(即上图中的Client 2和Client 3)此时匀为Follower
。
2. 放弃领导权
如果Leader
打算主动放弃领导权,直接删除/zkroot/leader
节点即可。
如果Leader
进程意外宕机,其与 ZooKeeper 间的Session
也结束,该节点由于是Ephemeral
类型的节点,因此也会自动被删除。
此时/zkroot/leader
节点不复存在,对于其它参与竞选的客户端而言,之前的Leader
已经放弃了领导权。
3. 感知领导权的放弃
由上图可见,创建节点失败的节点,除了成为Follower
以外,还会向/zkroot/leader
注册一个Watch
,一旦Leader
放弃领导权,也即该节点被删除,所有的Follower
会收到通知。
4. 重新选举
感知到旧Leader
放弃领导权后,所有的Follower
可以再次发起新一轮的领导选举,如下图所示:
从上图中可见:
- 新一轮的领导选举方法与最初的领导选举方法完全一样,都是发起节点创建请求,创建成功即为
Leader
,否则为Follower
,且Follower
会Watch
该节点 - 新一轮的选举结果,无法预测,与它们在第一轮选举中的顺序无关。这也是该方案被称为非公平模式的原因
非公平领导选举的优缺点
优点:
- 非公平模式实现简单,每一轮选举方法都完全一样
- 竞争参与方不多的情况下,效率高。每个
Follower
通过Watch
感知到节点被删除的时间不完全一样,只要有一个Follower
得到通知即发起竞选,即可保证当时有新的Leader
被选出
缺点:
给 ZooKeeper 集群造成的负载大,因此扩展性差。如果有上万个客户端都参与竞选,意味着同时会有上万个写请求发送给 Zookeper。ZooKeeper 存在单点写的问题,写性能不高。同时一旦Leader
放弃领导权,ZooKeeper 需要同时通知上万个Follower
,负载较大。
公平领导选举实现分布式锁
1. 选主过程
如下图所示,公平领导选举中,各客户端均创建/zkroot/leader
节点,且其类型为Ephemeral
与Sequence
。
由于是Sequence
类型节点,故上图中三个客户端均创建成功,只是序号不一样。此时,每个客户端都会判断自己创建成功的节点的序号是不是当前最小的。如果是,则该客户端为Leader
,否则即为Follower
。
在上图中,Client 1创建的节点序号为 1 ,Client 2创建的节点序号为 2,Client 3创建的节点序号为3。由于最小序号为 1 ,且该节点由Client 1创建,故Client 1为Leader
2. 放弃领导权
Leader
如果主动放弃领导权,直接删除其创建的节点即可。
如果Leader
所在进程意外宕机,其与 ZooKeeper 间的Session
结束,由于其创建的节点为Ephemeral
类型,故该节点自动被删除。
3. 感知领导权的放弃
与非公平模式不同,每个Follower
并非都Watch
由Leader
创建出来的节点,而是Watch
序号刚好比自己序号小的节点。
在上图中,总共有 1、2、3 共三个节点,因此Client 2 Watch /zkroot/leader1
,Client 3 Watch /zkroot/leader2
。(注:序号应该是10位数字,而非一位数字,这里为了方便,以一位数字代替)
一旦Leader
宕机,/zkroot/leader1
被删除,Client 2可得到通知。此时Client 3由于Watch
的是/zkroot/leader2
,故不会得到通知。
4. 重新选举
重新选举Client 2得到/zkroot/leader1
被删除的通知后,不会立即成为新的Leader
。而是先判断自己的序号 2 是不是当前最小的序号。在该场景下,其序号确为最小。因此Client 2成为新的Leader
。
这里要注意,如果在Client 1放弃领导权之前,Client 2就宕机了,Client 3会收到通知。此时Client 3不会立即成为Leader
,而是要先判断自己的序号 3 是否为当前最小序号。很显然,由于Client 1创建的/zkroot/leader1
还在,因此Client 3不会成为新的Leader
,并向Client 2序号 2 前面的序号,也即 1 创建Watch
。该过程如下图所示。
优缺点
优点:
- 扩展性好,每个客户端都只
Watch
一个节点且每次节点被删除只须通知一个客户端; - 旧
Leader
放弃领导权时,其它客户端根据竞选的先后顺序(也即节点序号)成为新Leader
,这也是公平模式的由来;
缺点:
- 实现相对复杂
- 延迟相对非公平模式要高,因为它必须等待特定节点得到通知才能选出新的
Leader
。
11. Zookeeper权限控制
Zookeeper采用ACL(AccessControlLists)策略来进行权限控制,类似于UNIX文件系统的权限控制。Zookeeper定义了如下5种权限:
- CREATE:创建字节点的权限
- READ:获取节点数据和字节点列表的权限
- WRITE:更新节点数据的权限
- DELETE:删除字节点的权限
- ADMIN:设置节点的ACL权限
其中,CREATE
和DELETE
这两种权限都是针对子节点的权限控制。
12. Zookeeper中session概念
Session 指的是 ZooKeeper 服务器与客户端会话。在 ZooKeeper 中,一个客户端连接是指客户端和服务器之间的一个 TCP 长连接。客户端启动的时候,首先会与服务器建立一个 TCP 连接,从第一次连接建立开始,客户端会话的生命周期也开始了。通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够向Zookeeper服务器发送请求并接受响应,同时还能够通过该连接接收来自服务器的Watch
事件通知。
Session的sessionTimeout
值用来设置一个客户端会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在sessionTimeout
规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。
在为客户端创建会话之前,服务端首先会为每个客户端都分配一个sessionID
。由于 sessionID
是 Zookeeper 会话的一个重要标识,许多与会话相关的运行机制都是基于这个sessionID
的,因此,无论是哪台服务器为客户端分配的sessionID
,都务必保证全局唯一。
13. Zookeeper的应用场景
- 统一命名服务:在分布式环境下,经常需要对应用或服务进行统一命名,便于识别。例如IP不容易记,使用域名。
- 统一配置管理:在分布式环境下,一般要求在一个集群下,所有节点的配置信息是一致的。在 Zookeeper 中,可以将配置信息写入到 Zookeeper 上的一个 Znode 中。对配置文件修改后,希望能够快速同步到各个节点上。每个客户端服务器监听这个 Znode,一旦 Znode 中的数据被修改,Zookeeper 将通知各个客户端服务器。
- 统一集群管理:分布式环境中,实时掌握每个节点的状态是必要的,要求可以根据节点实时状态作出一些调整。Zookeeper 可以实现实时监控节点状态变化。可将节点信息写入到 Zookeeper 的一个 Znode,监听这个 Znode 可以获取它实时状态变化。
- 服务器动态上下线:客户端能够实时洞察到服务器上下线的变化。服务器将是否在线的状态写入到 Znode 中,客户端获取到当前在线服务器列表,并注册监听,当服务器有上下线时就会发起事件通知。
- 软负载均衡:在Zookeeper中记录每台服务器的访问数,让访问数最少的服务器去处理最新的客户端请求。
参考内容
主要参考以来两篇博客以及相关博客推荐,因找的博客比较多,没注意记录,最后好多忘了在哪2333,如果有侵权,请及时联系我,非常抱歉。
https://github.com/Snailclimb/JavaGuide
https://github.com/CyC2018/CS-Notes