ZooKeeper采用ZAB(ZooKeeper Atomic Broadcast)一致性协议。
ZooKeeper保证如下分布式一致性特性。
a) 顺序一致性:同一客户端发起的请求,最终会严格按发起顺序应用到ZooKeeper中。
b) 原子性:所有请求的处理结果在整个集群所有机器上的应用情况是一致的。
c) 单一视图(Single System Image):客户端连接ZooKeeper任意一个服务器,看到的数据模型都是一致的。
d) 可靠性:应用了客户端请求之后,引起的数据变更被永久保存。
e) 实时性:仅保证在一定时间后,最终一致性。
ZooKeeper
集群角色:a) 没有Master/Slave,而引入三种角色。
b) Leader:为客户端提供读、写服务。通过Leader选举过程产生。
c) Follower:为客户端提供读、写服务,如果是写请求则转发给Leader。参与Leader选举过程。
d) Observer:与Follower相同,唯一区别是不参加Leader选举过程。
ZooKeeper API
命令
1. 创建节点:create [-s] [-e] path data [acl],-s顺序节点,-e临时节点。
2. 列出子节点:ls path [watch]。
3. 获取节点:get path [watch]。
4. 更新节点:set path data [version]。
5. 删除节点:delete path [version]。
6. 删除节点及其子节点:rmr path。
集群搭建:
a) “过半存货即可用”指如果ZooKeeper集群要对外可用,必须要有过半的机器正常工作并且彼此之间正常通信。即如果搭建一个允许F台机器宕机的集群,则要部署2xF+1台服务器。
b) 6台机器的集群可用性上并不比5台机器的集群高,所以产生了“官方推荐服务器数为奇数”的说法。
c) 需澄清:任意服务器数的ZooKeeper集群都能部署且正常运行。
分布式锁
1. 分布式锁是控制分布式系统之间同步访问共享资源的一种方式。
2. 分布式锁分为排它锁(Exclusive Lock,简称X锁,又称写锁、独占锁)和共享锁(Shared Lock,简称S锁,又称读锁)。
a) 排它锁类似JDK的synchronized和ReentrantLock。
b) 共享锁类似JDK的ReadWriteLock中的读锁。
3. 排它锁实现过程:与Master选举类似。所有客户端同时创建临时节点,创建成功的客户端获取了锁,创建失败的客户端Watcher监听节点开始等待,一旦该节点被移除(即排它锁已释放)则重复该过程。
4. 共享锁实现过程:
a) 创建持久节点/shared_lock。
b) 所有客户端根据需要的锁类型(R/W)创建临时顺序节点/shared_lock/[hostname-R/W-],如/shared_lock/[host1-R-0000000000]、/shared_lock/host1-W-0000000003。
c) 获取/shared_lock下的所有子节点。
d) 各客户端确定自己的节点顺序。
i. 当前客户端需要R锁时,如果无比自己序号小的子节点或所有比自己序号小的子节点都是R锁,则获取R锁成功;如果比自己序号小的子节点有W锁,则Watcher监听该W锁节点并等待。
ii. 当前客户端需要W锁时,如果自己序号是最小的子节点,则获取W锁成功,否则Watcher监听比自己序号小的子节点中序号最大的节点并等待。
e) 各客户端收到Watcher通知后,则获取锁成功。
5. Curator封装了分布式锁功能。
分布式队列
1. 业界分布式队列产品大多是消息中间件(或称消息队列),ZooKeeper也可实现分布式队列功能。
2. 分布式队列分为FIFO和Barrier两种:
a) FIFO即常见的队列;
b) Barrier类似JDK的CyclicBarrier,等待的数量达到一定值时才执行。
3. FIFO实现过程(类似共享锁):
a) 创建持久节点/queue_fifo。
b) 所有客户端创建临时顺序节点/queue_fifo/[hostname-],如/queue_fifo/host1-0000000000。
c) 获取/ queue_fifo下的所有子节点。
d) 各客户端确定自己的节点顺序:如果自己序号是最小的子节点,则执行;否则Watcher监听比自己序号小的节点中序号最大的节点并等待。
e) 收到Watcher通知后,则执行。
4. Barrier实现过程:
a) 创建持久节点/queue_barrier。
b) 所有客户端创建临时节点/queue_barrier/[hostname],如/queue_fifo/host1。
c) 获取/ queue_fifo下的所有子节点。
d) 如果子节点数大于或等于某值,则执行;否则Watcher监听节点/queue_barrier并等待。
e) 收到Watcher通知后,重复步骤c。