image.png

Zookeeper

ZooKeeper 是一个开放源码的分布式协调服务,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高 效、功能稳定的系统提供给用户。分布式应用程序可以基于 Zookeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。

腾讯定时任务,只会选一个Master执行。

zk集群:一个领导者(Leader),多个跟随者(Follower)组成的集群。

服务器角色

Leader
1、事务请求的唯一调度和处理者,保证集群事务处理的顺序性
2、集群内部各服务的调度者
Follower
1、处理客户端的非事务请求,转发事务请求给 Leader 服务器
2、参与事务请求 Proposal 的投票
3、参与 Leader 选举投票
Observer
1、3.0 版本以后引入的一个服务器角色,在不影响集群事务处理能力的基础上提升集群的非事务处理能力
2、处理客户端的非事务请求,转发事务请求给 Leader 服务器
3、不参与任何形式的投票

Server 工作状态

1、LOOKING:寻找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。
2、FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。
3、LEADING:领导者状态。表明当前服务器角色是 Leader。
4、OBSERVING:观察者状态。表明当前服务器角色是 Observer。

应用场景

  1. 分布式锁
  2. 服务注册和发现
    利用 Znode和 Watcher,可以实现分布式服务的注册和发现。最著名的应用就是阿里的分布式 RPC框架 Dubbo。
  3. 共享配置和状态信息
    Redis的分布式解决方案 Codis(豌豆荚),就利用了 Zookeeper来存放数据路由表和 codis-proxy 节点的元信息。同时 codis-config 发起的命令都会通过 ZooKeeper 同步到各个存活的 codis-proxy。

数据发布/订阅系统,即所谓的配置中心。基于 Zookeeper 的实现方式

  1. 数据存储:将数据(配置信息)存储到 Zookeeper 上的一个数据节点
  2. 数据获取:应用在启动初始化节点从 Zookeeper 数据节点读取数据,并在该节点上注册一个数据变更 Watcher
  3. 数据变更:当变更数据时,更新 Zookeeper 对应节点数据,Zookeeper会将数据变更通知发到各客户端,客户端接到通知后重新读取变更后的数据即可。

选举机制*-半数机制(paxos协议)

半数机制(paxos协议)
集群中半数以上机器存活,集群可用,所以Zookeeper适合安装奇数台服务器

内部选举步骤(Zab 的四个阶段)

1. 选举阶段 Leader election

节点在一开始都处于选举节点,只要有一个节点得到超过半数节点的票数,它就可以当选准 Leader,只有到达第三个阶段(也就是同步阶段),这个准 Leader 才会成为真正的 Leader。
Zookeeper 规定所有有效的投票都必须在同一个 轮次 中,每个服务器在开始新一轮投票时,都会对自己维护的 logicalClock 进行自增操作。

最大ZXID也就是节点本地的最新事务编号,包含epoch和计数两部分。epoch是纪元的意思,相当于Raft算法选主时候的term,标识当前leader周期,每次选举一个新的Leader服务器后,会生成一个新的epoch
所有节点处于Looking状态,各自依次发起投票,投票包含自己的服务器ID和最新事务ID(ZXID)
如果发现别人的 ZXID比自己大,也就是数据比自己新,那么就重新发起投票,投票给目前已知最大的 ZXID所属节点。
每次投票后,服务器都会统计投票数量,判断是否有某个节点得到半数以上的投票。如果存在这样的节点,该节点将会成为准Leader,状态变为 Leading。其他节点的状态变为Following

2. 发现阶段 Discovery

为了防止某些意外情况,比如因网络原因在上一阶段产生多个 Leader的情况。
Leader集思广益,接收所有 Follower发来各自的最新 epoch值。 Leader从中选出最大的 epoch,基于此值加1,生成新的 epoch分发给各个 Follower。
各个 Follower收到全新的 epoch后,返回 ACK给 Leader,带上各自最大的 ZXID和历史事务日志。 Leader选出最大的 ZXID,并更新自身历史日志。

这个阶段的主要目的是发现当前大多数节点接收的最新事务 Proposal,并且准 Leader 生成新的 epoch ,让 Followers 接收,更新它们的 acceptedEpoch。

3. 同步阶段 Synchronization

同步阶段主要是利用 Leader 前一阶段获得的最新 事务Proposal 历史,同步集群中所有的副本
Leader刚才收集得到的最新历史事务日志,同步给集群中所有的Follower。只有当半数Follower同步成功,这个准Leader才能成为正式的Leader。

4、广播阶段(Broadcast)

到了这个阶段,Zookeeper 集群才能正式对外提供事务服务,并且 Leader 可以进行消息广播。同时,如果有新的节点加入,还需要对新节点进行同步。

zk 节点宕机如何处理?*

Zookeeper 本身也是集群,推荐配置不少于 3 个服务器。Zookeeper 自身也要保证当一个节点宕机时,其他节点会继续提供服务。

  1. 如果是一个 Follower 宕机,还有 2 台服务器提供访问,因为 Zookeeper 上的数据是有多个副本的,数据并不会丢失;
  2. 如果是一个 Leader 宕机,Zookeeper 会选举出新的 Leader。

ZK 集群的机制是只要超过半数的节点正常,集群就能正常提供服务。只有在 ZK节点挂得太多,只剩一半或不到一半节点能工作,集群才失效。

3 个节点的 cluster 可以挂掉 1 个节点(leader 可以得到 2 票>1.5) 2 个节点的 cluster 就不能挂掉任何 1 个节点了(leader 可以得到 1 票<=1)

写数据流程

  1. 客户端发出写入数据请求给任意Follower。
  2. Follower把写入数据请求转发给Leader。
  3. Leader采用二阶段提交方式,先发送Propose广播给Follower。
  4. Follower接到Propose消息,写入日志成功后,返回ACK消息给Leader
  5. Leader接到半数以上ACK消息,返回成功给客户端,并且广播Commit请求给Follower

监听器原理

  1. Main进程
  2. 创建ZK客户端,会创建connet网络连接通信线程listener监听线程
  3. 通过connect线程将注册的监听事件发送给Zookeeper服务端
  4. 将监听事件添加到注册监听器列表
  5. 监听到有数据或路径变化,将消息发送给listener
  6. listener线程内部调用process方法
    丙-zookeeper - 图3

    zookeeper 是如何保证事务的顺序一致性的?

    zookeeper 采用了全局递增的事务 Id 来标识,所有的 proposal(提议)都在被提出的时候加上了 zxid,zxid 实际上是一个 64 位的数字,高32 位是 epoch用来标识 leader 周期,如果有新的 leader 产生出来,epoch会自增,低 32 位用来递增计数。当新产生 proposal 的时候,会依据数据库的两阶段过程,首先会向其他的 server 发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会开始执行.

    ZooKeeper 保证数据一致性 - Zab 协议*

    概念

Zab协议是为分布式协调服务Zookeeper专门设计的一种 支持崩溃恢复原子广播协议,Zab协议要求每个 Leader 都要经历三个阶段:发现,同步,广播。

1)Zab 协议需要确保那些已经在 Leader 服务器上提交(Commit)的事务最终被所有的服务器提交。
2)Zab 协议需要确保丢弃那些只在 Leader 上被提出而没有被提交的事务。

作用

  1. 使用一个单一的主进程(Leader)来接收并处理客户端的事务请求(也就是写请求),并采用了Zab的原子广播协议,将服务器数据的状态变更以 事务proposal (事务提议)的形式广播到所有的副本(Follower)进程上去。
  2. 保证一个全局的变更序列被顺序引用。
  3. 当主进程出现异常的时候,整个zk集群依旧能正常工作。

在Zab协议中,只要超过半数的Follower服务器进行了正确的反馈后(也就是收到半数以上的Follower的Ack请求),那么 Leader 就会再次向所有的 Follower服务器发送 Commit 消息,要求其将上一个 事务proposal 进行提交。

Zab协议内容 (崩溃恢复 和 消息广播)

通过 Zab 协议(原子广播协议)来保证分布式事务的最终一致性。 在 ZooKeeper 集群中,所有客户端的请求都是写入到 Leader 进程中的,然后,由 Leader 同步到其他节点,称为 Follower。在集群数据同步的过程中,如果出现 Follower 节点崩溃或者 Leader 进程崩溃时,都会通过 Zab 协议来保证数据一致性。

消息广播

Zab协议中 Leader 等待 Follower 的ACK反馈消息是指“只要半数以上的Follower成功反馈即可,不需要收到全部Follower反馈”。

  1. zookeeper 采用 Zab 协议的核心,就是只要有一台服务器提交了 Proposal,就要确保所有的服务器最终都能正确提交 Proposal。这也是 CAP/BASE 实现最终一致性的一个体现。
  2. Leader 服务器与每一个 Follower 服务器之间都维护了一个单独的 FIFO 消息队列进行收发消息,使用队列消息可以做到异步解耦。 Leader 和 Follower 之间只需要往队列中发消息即可。如果使用同步的方式会引起阻塞,性能要下降很多。

    有序性是 zookeeper 中非 常重要的一个特性,所有的更新都是全局有序的,每个更新都有一个唯一的时间戳,这个时间戳称为 zxid(Zookeeper Transaction Id)。而读请求只会相对于更新有序,也就 是读请求的返回结果中会带有这个 zookeeper 最新的 zxid

崩溃恢复

一旦 Leader 服务器出现崩溃或者由于网络原因导致 Leader 服务器失去了与过半 Follower 的联系,那么就会进入崩溃恢复模式。

Zab 协议崩溃恢复要求满足以下两个要求:

  1. 确保已经被 Leader 提交的事务必须最终被所有的 Follower 服务器提交。
  2. 确保丢弃已经被 Leader 提出的但是没有被提交的事务。

leader选举
  1. 新选举出来的 Leader 不能包含未提交的 Proposal 。
  2. 新选举的 Leader 节点中含有最大的 zxid

数据恢复
  1. 完成 Leader 选举后(新的 Leader 具有最高的zxid),在正式开始工作之前(接收事务请求,然后提出新的 Proposal),Leader 服务器会首先确认事务日志中的所有的 事务是否已经被集群中过半的服务器 Commit。
  2. Leader 服务器需要确保所有的 Follower 服务器能够接收到每一条事务的 Proposal ,并且能将所有已经提交的事务 Proposal 应用到内存数据中。等到 Follower 将所有尚未同步的事务 Proposal 都从 Leader 服务器上同步过啦并且应用到内存数据中以后,Leader 才会把该 Follower 加入到真正可用的 Follower 列表中。

ZAB 和 Paxos 算法的联系与区别?

相同点:
1、两者 都存 在一 个类 似于 Leader 进程 的角 色 ,由其 负责 协调 多个 Follower 进程的运 行
2、Leader 进程 都会 等待 超过 半数 的 Follower 做出 正确 的反 馈后 ,才会 将一 个提案进 行提 交
3、 ZAB 协议 中, 每个 Proposal 中都 包含 一个 epoch 值来 代表 当前 的 Leader周期 ,Paxos 中名 字为 Ballot
不同点:
ZAB 用来 构建 高可 用的 分布 式数 据主 备系 统( Zookeeper), Paxos 是用 来构 建分布 式一 致性 状态 机系 统。

Watcher 机制 — 数据变更通知(一次)

Zookeeper 允许客户端向服务端的某个 Znode 注册一个 Watcher 监听,当服务端的一些指定事件触发了这个 Watcher,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然后客户端根据 Watcher 通知状态和事件类型做出业务上的改变。

工作机制

1、客户端注册 watcher
2、服务端处理 watcher
3、客户端回调 watcher

Watcher 特性总结

1、一次性
无论是服务端还是客户端,一旦一个 Watcher 被触发,Zookeeper 都会将其从相应的存储中移除。这样的设计有效的减轻了服务端的压力,不然对于更新非常频繁的节点,服务端会不断的向客户端发送事件通知,无论对于网络还是服务端的压力都非常大。
2、客户端串行执行
客户端 Watcher 回调的过程是一个串行同步的过程。
3、轻量
3.1、Watcher 通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。
3.2、客户端向服务端注册 Watcher 的时候,并不会把客户端真实的 Watcher 对象实体传递到服务端,仅仅是在客户端请求中使用 boolean 类型属性进行了标记。
4、watcher event 异步发送 watcher 的通知事件从 server 发送到 client 是异步的,这就存在一个问题,不同的客户端和服务器之间通过 socket 进行通信,由于网络延迟或其他因素导致客户端在不通的时刻监听到事件,由于 Zookeeper 本身提供了 ordering guarantee,即客户端监听事件后,才会感知它所监视 znode发生了变化。所以我们使用 Zookeeper 不能期望能够监控到节点每次的变化。Zookeeper 只能保证最终的一致性,而无法保证强一致性。
5、注册 watcher getData、exists、getChildren
6、触发 watcher create、delete、setData
7、当一个客户端连接到一个新的服务器上时,watch 将会被以任意会话事件触发。当与一个服务器失去连接的时候,是无法接收到 watch 的。而当 client 重新连接时,如果需 要的话,所有先前注册过的 watch,都会被重新注册。通常这是完全透明的。只有在一个特殊情况下,watch 可能会丢失:对于一个未创建的 znode的 exist watch,如果在客户端断开连接期间被创建了,并且随后在客户端连接上之前又删除了,这种情况下,这个 watch 事件可能会被丢失。

ZooKeeper之两阶段提交(2PC)*

2PC:在分布式系统中,每一个机器节点虽然能够明确的知道自己在进行事物操作过程的结果是失败或成功,但却无法直接获取其他分布式节点的操作结果,因此事物操作需要跨越多个分布式节点的时候,需要引入一个协调者统一调度所有节点的执行逻辑。

阶段一:提交事物请求(预提交)

  1. 事物询问
    协调者向所有的参与者发送事物内容,询问是否可以执行事物操作,并开始等待各参与者的响应。
  2. 执行事物
    各参与者节点执行事物操作,并将Undo和Redo信息记入事物日志中
  3. 各参与者向协调者反馈事物询问的响应(ACK)
    如果参与者成功执行了事物操作,那么就反馈给协调者Yes响应,表示事物可以执行;如果参与者没有成功执行事物,那么就反馈给协调者No响应,表示事物不可以执行。

    阶段二:执行事物请求

    在阶段二中协调者会根据参与者的反馈情况来决定最终是否可以进行事物操作,其中有两种可能。
    假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事物响应。

  4. 发送提交请求。
    协调者向所有参与者节点发出Commit请求

  5. 事物提交
    参与者接收到Commit请求后,会正式执行事物提交操作,并在完成提交之后释放在整个事物执行期间占有的事物资源。
  6. 反馈事物提交结果
    参与者在完成事物提交之后,向协调者发送Ack消息。
  7. 完成事物
    协调者接收到所有参与者反馈的Ack消息后,完成事物。

    优缺点

  • 优点:原理简单,实现方便
  • 缺点:同步阻塞,单点问题,脑裂,太过保守。