Zookeeper知识点详解

Zookeeper知识点详解


目录

  1. ZooKeeper 集群原理

  2. ZooKeeper 分布式锁

  3. ZooKeeper 分布式事务

  4. ZooKeeper 选举原理

  5. Paxos 协议

  6. ZAB 协议

  7. ZooKeeper 会话管理

  8. ZooKeeper 的 Watcher 机制

  9. ZooKeeper 的应用场景


1. ZooKeeper 集群原理

关于 ZooKeeper,官网给出的一句话定义:

A Distributed Coordination Service for Distributed Applications

简单地说,ZooKeeper 是一个分布式应用程序协调服务。
在这里插入图片描述

ZooKeeper 集群设计原则:

  1. 最终一致性:客户端无论连接哪个 ZK 节点,读到的数据最终一定是相同的。
  2. 可靠性:客户端无论往哪个 ZK 节点写数据,数据最终一定会同步到其他节点。
  3. 实时性:由于 ZK 复制数据带来一定的延迟,客户端可以调用 sync() 拉取最新数据。
  4. 独立性:ZK 集群允许多客户端并发执行任务。
  5. 原子性:对于同一份数据,不存在一部分节点写成功,另一部分节点写失败。
  6. 顺序性:ZK 为每一个写事务分配全局自增 zxid,保证写入消息依然保持有序。

2. ZooKeeper 分布式锁

ZooKeeper 分布式锁有两个核心要素:临时有序节点和 Watcher 事件监听。

首先,什么是临时有序节点?ZK 底层数据结构是一棵树,由节点 Znode 组成,Znode 分四种类型:

  1. 持久节点 (PERSISTENT):默认类型,节点一旦创建,即便客户端失去连接,节点依然保留。
  2. 临时节点(EPHEMERAL) :与持久节点相反,客户端失去连接,节点被 ZK 自动删除。
  3. 持久节点顺序节点(PERSISTENT_SEQUENTIAL):ZK 按创建时间给持久节点编号。
  4. 临时顺序节点(EPHEMERAL_SEQUENTIAL):ZK 按创建时间给临时节点编号。
public enum CreateMode {
  PERSISTENT(0, false, false),
  PERSISTENT_SEQUENTIAL(2, false, true),
  EPHEMERAL(1, true, false),
  EPHEMERAL_SEQUENTIAL(3, true, true);
}

其次,什么是 Watcher?简单地说,Watcher 是一种事件监听机制。客户端在读写数据时,可以对 ZK 节点注册事件监听器 Watcher。一旦 ZK 节点数据或结构变化,ZooKeeper 会触发相应的事件 WatchEvent,通知客户端 Watcher 接口的回调方法 process()。

public interface Watcher {
    abstract public void process(WatchedEvent event);
}

接下来,我们看看什么是分布式锁?相对于 JVM 锁,分布式锁的特点是跨 JVM 的,这两张图可以说明。
在这里插入图片描述
下面,举例说明 ZooKeeper 是如何实现分布式锁的。

假设有三个进程同时访问一个临界区资源,我们需要 ZooKeeper 分布式锁做并发控制,同一时刻只允许同一个进程访问。我们把三个进程当成三个 ZK 的客户端,分别创建三个 ZK 临时有序节点:Lock1、Lock2、Lock3,挂在 ParentLock 节点下面,其中 Lock1 节点编号最小,Lock3 节点编号最大。已知 Client1 获取锁,Client2 与 Client3 分别注册 Watch 监听器,其中 Client2 监听 Lock1 节点,Client3 监听 Lock2 节点。
在这里插入图片描述
假设 Client1 完成任务,断开连接,释放锁,ZK 自动删除 Lock1 节点,产生事件,触发 Client2 的监听器 Watcher 回调。此时,Client2 会再次查询 ParentLock 下面的所有节点,确认自己创建的节点 Lock2 是否为最小节点,如果是,则 Client2 顺利成章获取了锁。
在这里插入图片描述
最后看一下获取锁的代码:

public boolean tryLock() {
    String node = zk.create("ParentLock/", CreateMode.EPHEMERAL_SEQUENTIAL);
    List<String> allNodes = zk.getChildren("ParentLock");
    Collections.sort(allNodes);
    if(allNodes.get(0).equals(nodeName)){
      //当前结点是最小的节点,获取锁成功
        return true;
    } else{
      //监听最小的结点
        zk.exists(allNodes.get(0), new Watcher() {
            public void process(WatchedEvent event) {
                if(event.getType() == EventType.NodeDeleted){
                        ...
                }
        }
        return false;               
    }

3. ZooKeeper 分布式事务

ZooKeeper 实现分布式事务,类似于两阶段提交,总共分四个步骤:

步骤 #1:客户端先向 ZK 节点发送写请求。
步骤 #2:ZK 节点将写请求转发给 Leader 节点,Leader 广播给集群要求投票,等待确认。
步骤 #3:Leader 收到确认,统计投票,票数过半则提交事务。
步骤 #4:事务提交成功后,ZK 节点告知客户端。
在这里插入图片描述


4. ZooKeeper 选举原理

先来明确一下概念:

sid(serverId):比如有三台服务器,编号分别是 1、2、3。编号越大,在选择算法中的权重越大。
zxid(最新的事务 ID ):ID 值越大说明数据越新,在选举算法中数据越新权重越大。
epoch (逻辑时钟 ):同一轮投票的逻辑时钟值是相同的。每投完一次票这个数据就会增加。
选举状态:LOOKING 竞选;FOLLOWING 随从,投票;OBSERVING 观察,不投票;LEADING 领导者。
vote_sid:对方投票推举 Leader 的服务器 sid。
vote_zxid:对方投票推举 Leader 的服务器 zxid。
self_sid:当前服务器的 sid。
self_zxid:当前服务器的 zxid。

规则如下:

  1. 如果 vote_zxid 大于自己的 self_zxid,就认可当前收到的投票,并再次将该投票发送出去。
  2. 如果 vote_zxid 小于自己的 self_zxid,那么就坚持自己的投票,不做任何变更。
  3. 如果 vote_zxid 等于自己的 self_zxid,那么就对比两者的 sid,重复 规则1 或者 规则2。

我们假设 ZooKeeper 集群由 5 台机器组成,sid 分別为 1、2、3、4 和 5,zxid 分别为 9、9、9、8 和 8,2 号机器是 Leader。某一时刻,1 号和 2 号机器宕机,因此集群开始进行 Leader 选举。

第一轮投票,epoch=1。

由于机器彼此不知对方的状态,因此每台机器都将票投给自己。投票情况分别为:(3, 9)、(4, 8)和(5, 8)。每台机器都把投票发出后,同时也会接收到来自另外两台机器的投票。
在这里插入图片描述
第二轮投票,epoch=2。

对干 3 号机器,接收(4, 8)和(5, 8)两张投票,发现 self_zxid=9 大于 vote_zxid=8,因此无需变更。

对于 4 号机器,接收(3, 9)和(5, 8)两张投票,发现 self_zxid=8 小于 vote_zxid=9,认可并变更投票为(3, 9),再次将这张票投出去。

5 号机器与 4 号类似,认可并变更投票为(3, 9),再次将这张票投出去。

两轮投票过后,投票桶的票数为 3,已过半数,3 号晋升为 Leader,其余退化为 Follower。


5. Paxos 协议

ZooKeeper 分布式一致性算法的原型,就是 Paxos 协议。下面简单分析一下 Paxos 协议。Paxos 中有三类角色 Proposer、Acceptor 及 Learner,相当于 ZooKeeper 集群中的 Leader、Follower 和 Observer。

简单地说,Paxos 的原理是:Proposer 将发起提案(value)给所有 Accpetor,超过半数 Accpetor 获得批准后,Proposer 将提案写入 Accpetor 内,最终所有 Accpetor 获得一致性的确定性取值,且后续不允许再修改。

这 4 类消息对应于 Paxos 算法的两个阶段 4 个过程,如下图:

在这里插入图片描述
Paxos 在原作者的 Paxos Made Simple 中内容是比较精简的:


Phase 1

(a) A proposer selects a proposal number n and sends a prepare request with number n to a majority of acceptors.
(a) Proposer 选择一个提议编号 n,向超半数 Acceptor 广播 Prepare(n)请求。

(b) If an acceptor receives a prepare request with number n greater than that of any prepare request to which it has already responded, then it responds to the request with a promise not to accept any more proposals numbered less than n and with the highest-numbered pro-posal (if any) that it has accepted.
(b) Acceptor 接收到 Prepare(n)请求,若提议编号 n 比之前接收的 Prepare 请求都要大,则承诺将不会接收提议编号比 n 小的提议,并且带上之前 Accept 的提议中编号小于 n 的最大的提议,否则不予理会。

Phase 2

(a) If the proposer receives a response to its prepare requests (numbered n) from a majority of acceptors, then it sends an accept request to each of those acceptors for a proposal numbered n with a value v , where v is the value of the highest-numbered proposal among the responses, or is any value if the responses reported no proposals.
(a) Proposer 得到了 Acceptor 响应,如果未超过半数 Accpetor 响应,直接转为提议失败;如果超过多数 Acceptor 的承诺,又分为不同情况:如果所有 Acceptor 都未接收过值(都为 null),那么向所有的 Acceptor 发起自己的值和提议编号 n;如果有部分 Acceptor 接收过值,那么从所有接受过的值中选择对应的提议编号最大的作为提议的值,提议编号仍然为 n。但此时 Proposer 就不能提议自己的值,只能信任 Acceptor 通过的值 v,维护一但获得确定性取值就不能更改原则;

(b) If an acceptor receives an accept request for a proposal numbered n, it accepts the proposal unless it has already responded to a prepare request having a number greater than n.
(b) Acceptor 接收到提议后,如果该提议版本号不等于自身保存记录的版本号(第一阶段记录的),不接受该请求,相等则写入本地。


6. ZAB 协议

ZAB 是 ZooKeeper Atomic Broadcast 简称,是 ZooKeeper 实现一致性的理论依据。原文写得非常简单,只有非常笼统的框架,没有细节。

ZAB 分为两个部分:Broadcast 和 Recovery。

当系统启动或者leader服务器出现故障等现象时,进入故障恢复模式。将会开启新的一轮的选举。选举产生的leader会与过半的follower进行同步,保持数据一致。当同步结束后,退出恢复模式,进入消息广播模式。当一台遵循ZAB协议的服务器启动后,如果检测到有leader在广播消息,会自动进入恢复模式,当其完成与leader的同步以后,进入消息广播模式。如果集群中的非leader 节点收到客户端请求,非leader会先将请求发送到leader服务器。

先说一下 Broadcast 广播机制。

Leader 接受所有写请求;超过半数 Follower 进行 Ack 确认后,Leader 发送 Commit。类似于简化版的二阶段协议。Leader 用 zxid 保证消息的顺序性。Follower 存入硬盘后 Ack,在接收到 Commit 后开始服务 Client。Client 读 in-memory 的数据。
在这里插入图片描述
再说一下 Recovery 恢复机制。

刚刚我们说消息广播过程中,Leader 崩溃(崩溃即 Leader 失去与过半 Follwer 的联系)怎么办?还能保证数据一致吗?

针对这些问题,ZAB 定义了 2 个原则:

  • ZAB 协议确保那些已经在 Leader 提交的事务最终会被所有服务器提交。
  • ZAB 协议确保丢弃那些只在 Leader 提出/复制,但没有提交的事务。

在这里插入图片描述


7. ZooKeeper 会话管理

Session 指的是 ZooKeeper 服务器与客户端会话,换句话说,指的是客户端和服务端之间的一次 TCP 长连接。

客户端启动时,会与服务器建立 TCP 连接,此时客户端会话的生命周期就开始了。通过 Session,客户端能够用 ping-pong 心跳检测与服务端保持连接,客户端可以向 ZK 发送请求并接受响应,同时 ZK 还能通过 Watch 向客户端推送通知。

Session 的 sessionTimeout 参数用来设置一个客户端会话的超时时间。由于网络故障或客户端主动断开连接时,只要在 sessionTimeout 有效期内重连任意机器,那么此前创建的会话仍然有效。

为客户端创建会话之前,服务端首先会为每一个客户端分配一个 sessionID。由于 sessionID 是 ZooKeeper 会话的重要标识,许多与会话相关的运行机制都是基于这个 sessionID 的,因此无论是哪台服务器为客户端分配 sessionID,都要务必保证全局唯一。


8. ZooKeeper 的 Watcher 机制

Watcher 是 ZooKeeper 的一个重要特性。ZooKeeper 允许用户在指定的节点上注册一些 Watcher,并且在一些特定事件触发的时候,ZooKeeper 服务端会将事件通知到感兴趣的客户端上,该机制是 ZooKeeper 实现分布式协调服务的重要特性。

public interface Watcher {
    abstract public void process(WatchedEvent event);
}

ZooKeeper 的 Watcher 有几个特点:

  • 一次性:一旦一个 Watcher 被触发,ZooKeeper 就会将其从相应的存储中移除,所以使用时要反复注册。
  • 客户端串行:执行 Watcher 回调的过程是一个串行执行的过程,从而保证了顺序。
  • 轻量:客户端向服务端注册 Watcher 的时候,并不会把客户端真实的 Watcher 对象实体传递到服务端,仅仅是在客户端请求中使用 boolean 类型属性进行了标记。Watcher 通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。

9. ZooKeeper 的应用场景

发布/订阅

应用启动时主动去 ZooKeeper 获取配置信息,并注册一个 Watcher 监听配置发生变更时,ZooKeeper 服务端会通知所有订阅的客户端,客户端收到通知后,主动到服务端获取最新数据。

全局唯一

ID 利用顺序节点的特性,创建一个顺序节点,根据返回的完整节点名,作为 ID 即可。

Master 选举

  • 开始选举,即所有机器都作为 ZooKeeper 向 ZooKeeper 集群请求创建一个临时节点,成功创建该节点的机器,成为 Master。
  • 其他创建失败的客户端对应的机器,都会在该节点上注册一个 Watcher,用于监控当前的 Master 机器是否存活。
  • 一旦发现当前的 Master 挂了(即收到 Watcher 事件通知节点被删除),则重新选举。

分布式锁

  • 排他锁:和 Master 选举类似,利用临时节点的特性。创建临时节点成功的客户端获得锁,客户端意外挂掉则临时节点被自动删除或者是正常结束业务逻辑然后主动删除节点,此时 ZooKeeper 会通知其他客户端再次争夺锁。
  • 共享锁:利用顺序节点的特性。对于读写请求,分别创建临时顺序节点。下面重要的是判断读写顺序——对于读请求,如果有比自己序号小的写请求,则进入等待,否则执行读取逻辑;对于写请求,如果自己不是最小的序号,则进入等待。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值