Zookeeper 技术内幕
作者:互联网
文章目录
一、Zookeeper 核心概念概述
Zookeeper是一个分布式协调服务框架,是一个分布式数据一致性的解决方案。从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper就将负责通知已经在Zookeeper上注册的那些观察者做出相应的反应。
集群角色
在分布式系统中,集群的每一台机器一般都会有自己的角色,例如最为经典的 Master/Slave模式,就将控制写操作的机器称为 Master,而通过异步复制获取数据并且提供读服务的机器称为 slave。
Zookeeper 则是通过引入 Leader、Follower 以及 Observer 三种角色。
- 通过选举过程选定 Leader 机器,可以提供读写服务。
- Follower 和 Observer 都能提供读服务,但是 Observer 并不参与选举过程,也不参与写操作的过半写成功策略。
会话
Zookeeper 中一个客户端连接指的是客户端和服务器端之间的一个 TCP 连接。从连接的建立,一个客户端的会话周期也开始了。
并且会通过心跳检测来保证有效会话。
节点(ZNode)
节点不仅指集群中的某一台机器,在 Zookeeper 中也指数据模型中的数据单元。
Zookeeper 将所有数据存储在内存中,数据模型是一棵树。
ZNode 可以分为持久节点和临时节点两类。
- 持久节点指结点一旦被创建,除非主动移除,不然会一直存在。
- 临时节点指生命周期和客户端会话绑定,会话终止则消失。
版本
每个ZNode 会维护一个 Stat 数据结构,记录了当前节点,子节点以及 ACL 的版本号。
事件监听器(Watcher)
Zookeeper 可以在某些节点上添加 Watcher 监听感兴趣的事件,如果发送事件则通知对应的客户端。
ACL
通过 ACL (Access Control Lists)策略来对节点进行权限控制。
二、ZAB 协议
Zookeeper 使用 Zookeeper Atomic Broadcast(ZAB,ZooKeeper原子消息广播协议) 的协议作为其数据一致性的核心算法。
Zookeeper 使用单一的主进程(Leader)接受并处理客户端的事务请求,然后采用 ZAB 的原子广播协议,将服务器数据的状态变更广播到所有副本进程(Follower)。
1. 消息广播
ZAB协议的消息广播过程使用的是一个原子广播协议,类似于一个二阶段提交过程。
- Leader 对于客户端的请求会生成相应的事务 Proposal 并广播发送给所有 Follower ,广播之前,Leader 为每个 proposal 分配一个唯一递增ID(ZXID)来保证消息的先后顺序。
- 另外 Leader 会为每个 Follower 分配一个消息队列,将事务发送到消息队列中,Follower 串行接受消息,并将 Proposal 写入磁盘,成功则返回一个 ACK 消息。
- 如果超过一半的 Follower 返回 ACK 则提交整个事务。、
但是,可能存在 Leader 崩溃退出而带来的数据不一致的问题,则需要通过崩溃恢复来解决。
2. 崩溃恢复
如果一个 Leader 服务器突然崩溃,或者失去与一半 Follower 的联系,就会进入崩溃恢复,需要选举新的Leader 并且 让其他 Follower 感知到新的 Leader。
为了保证数据的一致性,ZAB 协议需要保证以下两条特性:
- ZAB协议需要确保那些已经在 Leader 服务器上提交的事务最终被所有服务器都提交,也就是响应超过一半 ACK 的事务应该被提交。
- ZAB协议需要确保丢弃那些只在 Leader 服务器上被提出的事务,也就是Leader 提出一个提案还没有广播就崩溃了,当原 Leader 恢复到集群时,应该丢弃该提案。
所以,选举算法会选举出 ZXID 也就是事务 ID 最大的节点,就能保证该节点拥有系统中最全的事务消息。
再选取出 Leader 节点之后,就会进入数据同步的过程,对于正常的数据同步 Follower 机器,Leader 为每个 Follower 创建一个队列,并为每个 Follower 发送没有同步的事务,并带着 Commit 指令,表示已经提交。来保证集群系统数据的一致性。
而对于需要丢弃的事务,也就是之前 Leader 产生但是还没有发送给 Follower 的提案是如何丢弃的呢?
这又要回到 ZAB 协议的事务编号 ZXID,这是一个64位的数字,低 32 位是递增的计数器,而高 32 位则是代表 Leader 周期的 epoch 编号,也就是每更换一个 Leader 会 +1 ,代表了 Leader 的纪元。当之前的Leader恢复之后,发现之前的事务存在编号相同但是纪元更新的提案,则会进行回退。
三、系统模型
1. 数据模型
Zookeeper 的视图结构,类似于 Unix 系统中的文件系统,将文件目录抽象成了数据节点,也就是 ZNode,ZNode 可以存储数据,也可以挂在子节点,所以形成树形的层次化命名空间。
另外,对于可以改变服务器状态的操作,称为事务,包括对于数据节点的创建删除,内容更新等。
2. 节点特性
Zookeeper 中每个节点都有对应的生命周期,取决于节点的类型。
在ZooKeeper 中,节点类型可以分为持久节点(PERSISTENT)、临时节点(EPHEMERAL)和顺序节点(SEQUENTIAL)三大类,具体在节点创建过程中,通过组合使用,可以生成以下四种组合型节点类型:
-
持久节点(PERSISTENT)
- 最常见的节点类型,被创建之后会一直存在于 Zookeeper 服务器,直到有主动的删除操作来清除节点。
-
持久顺序节点(PERSISTENT_SEQUENTIAL)
- 持久顺序节点在持久节点的基础上,加上了顺序的特性,也就是在创建节点的时候会根据先后顺序设置标记,为节点名加上一个数字后缀来标识。
- 顺序性在锁定和同步中起到重要作用,可以根据序号来判断事务执行顺序等。
-
临时节点(EPHEMERAL)
- 临时节点的生命周期与客户端会话绑定,随着客户端会话断开而消失,另外,临时节点不能挂在子节点,只能作为叶子节点。
-
临时顺序节点(EPHEMERAL_SEQUENTIAL)
- 在临时节点的基础上加上了顺序性。
另外,可以通过 stat 查看节点的状态。
3. 版本
Zookeeper 为数据节点引入版本的概念,每个节点主要保存了三种版本的信息:
version
:指的是当前节点的版本号。cversion
:当前节点子节点的版本号。aversion
:当前节点 ACL(也就是权限控制)的版本号。
因为 ZooKeeper 采用的 ZAB 原子消息广播协议,所以需要利用版本号来保证分布式情况下的原子性,版本号记录的是每个节点对应数据的修改次数,例如某个节点刚被创建,版本号就是0,修改一次则 + 1,每次进行事务操作的时候,就会先检查版本号,如果发现不是预期的当前版本,就会抛出 BadVersionExcption 异常。成功进行事务操作之后,就会对版本号进行一次更新。
4. Watcher 监视器
ZooKeeper 可以通过 Watcher 的机制来实现发布订阅的功能,客户端可以向服务器端注册一个 Watcher 监听,相当于注册感兴趣的事件,当该事件发生,则出发 Watcher ,就会向客户端发送一个对应的通知进行处理。
该机制主要的 Watcher 存储在客户端的 WatchManager 中,当触发事件之后,服务器端发送通知,客户端从 WatchManager 中取出 Watcher 对象执行对应的回调函数。
也就是回调 process() 函数。
abstract public void process(WatchedEvent event);
Zookeeper 服务器将触发事件包装为 WatchedEvent 返回放置到Watcher 中,客户端调用回调函数,取出对应事件进行处理。
5. Access Control List 访问控制列表
为了保障ZooKeeper 内数据的安全,避免因为误操作而导致的分布式系统异常,Zookeeper 提供一套 ACL 的机制来保证数据的安全。
ACL 机制主要包括三方面来实现:
-
权限模式(Scheme)
:用来确定权限验证过程中使用的检验策略。- IP:通过匹配 IP 网段的方式来进行权限控制。
- Digest:最常用的控制方式,使用标识来区分,例如“username:password”。
- World:一种开放式的方式,访问权限对所有用户开放。
- Super:超级用户,相当于管理员,可以进行任何操作。
-
授权对象 ID
:也就是权限赋予的对应实体,不同的模式下是不同的,例如 IP 模式下就是对应的 IP 主机,Digest 模式下就是自定义的授权实体。 -
权限(Permission)
:指通过权限检查之后可以进行的操作,在 ZooKeeper 中主要包括,创建、删除、读、写和管理。
四、Leader 选举
ZooKeeper Leader的选举是极其重要的技术之一,也是保障分布式数据一致性的关键所在,主要包括了服务器启动时的 Leader 选举,以及 Leader 崩溃之后的选举。
1. 服务器启动时期的Leader选举
将服务器集群中的机器启动并且可以相互通信之后发现没有Leader 节点,就进入 LOOKING 状态,并且可以开始进行选举 Leader 的过程。
主要流程如下:
- 每台机器会发出一个投票,基本内容包括自己的机器 ID,myid,以及处理的事务 ID,ZXID。并将该票投出给服务器的每一个节点,包括自己。
- 每台服务器接收来自别的服务器的投票,验证有效性,是否是本轮投票,以及是否来自 LOOKING 状态的机器。
- 每台服务器接收到别的服务器的选票之后,就会和自己的进行比对,优先选择 ZXID 大的,如果一样,则选取 myid 大的。选出更优质的票后,更新自己的投票为选举该节点作为 Leader 并会将该票继续投出给其余节点。
- 当超过一半的机器投向同一个节点之后,该节点就会变成 Leader 节点。Follower 节点就会更改状态为 FOLLOWING,Leader 就会改变状态为 LEADING。
2. 服务器运行期间的Leader选举
在提供服务期间,一旦Leader崩溃或者失去大于一半节点的联系,则该集群会无法对外提供服务,而进入新一轮 Leader 选举,选举过程和启动时大体相同,只不过 ZXID 不再是0,所以会选择事务ID更大的Follower 作为 Leader,因为他有集群最全的数据。
五、服务器角色
以上的内容应该已经清楚的知道了ZooKeeper 服务器主要包括三个角色:Leader、Follower 和 Observer。
Leader
:是整个集群工作机制的核心。主要工作包括:- 事务请求的唯一调度和处理者,保证集群事务处理的顺序性。
- 集群内部各服务器的调度者。
- 另外,用责任链模式来处理客户端请求也是Leader的一个特色。
Follower
:是集群中 Leader 的追随者,主要工作包括:- 处理客户端非事务请求,转发事务请求给 Leader 服务器。
- 参与事务请求 Proposal 的投票。
- 参与 Leader 选举投票。
Observer
- Observer 观察Zookeeper 集群的变化情况并将新的状态同步。
- Observer 的所有工作机制都与 Follower 一样,唯一的不同在于 Observer不参与各种投票。只是为了提高非事务请求处理的并发度。
六、请求处理
1. 写数据请求
ZK 客户端可以使用 SetData接口来进行写数据请求,改变数据节点的状态。大体分为请求的预处理,事务处理、事务应用以及请求响应四大步骤。
-
请求预处理
:进行一些校验以及生成真正进行的事务操作。- 通过 ZooKeeper 的IO层接收客户端的请求,判断是否是创建会话请求,如果不是,则交给 PreRequestProcessor 处理器处理。
- 创建对应的会话头。
- 进行会话的检查。
- 将请求反序列化,创建生成服务器可以读懂的 ChangeRecord 记录。
- 进行 ACL 权限以及版本号的检查,确保权限以及原子性操作。
- 封装请求的事务体 SetDataTxn
- 最后将生成的事务操作放入到 outstandingChanges 队列中,该队列存放了当前正在处理的事务请求。
-
事务处理
:将请求交给ProposalRequestProcessor处理器,真正的执行事务流程。执行阶段又包括是哪个子流程:- Sync 流程:也就是各个节点同步记录事务日志的流程,每个节点对该事务ID对应的事务进行校验和写入磁盘,成功则会返回对应的ACK消息。
- Proposal 流程:也就是提案流程,向其他节点发起执行该事务的提案,并等待其他节点执行 Syn 流程并发回反馈投票。
- Commit 流程:提交流程,在上个过程中获得投票如果大于一半,则真正允许数据节点的修改,通过原子消息广播同步数据到每个节点。
-
事务应用
- 结束事务的操作,真正应用修改后的数据节点。
-
请求响应
- 创建对应的响应头和响应体,填充数据,并通过序列化之后使用IO响应给客户端。
2. 事务转发请求
在 ZK 集群中,只有 Leader 节点能够处理事务请求,并不是所有的客户端都连接上 Leader 节点,所以需要对事务进行转发,当 Follower 节点或者 Observer 节点接受到事务请求之后,会检查是否是事务请求,如果是,则转发到Leader 节点进行处理。
3. 读数据请求
非事务请求的处理大体与写操作一样,但是少了很多校验转发操作,从相应数据节点中获取数据,封装为响应传送回客户端。
参考:
《从Paxos到Zookeeper :分布式一致性原理与实践》
标签:事务,Zookeeper,技术,节点,内幕,服务器,Leader,客户端 来源: https://blog.csdn.net/weixin_48922154/article/details/120208128