概要
- ZooKeeper是一个分布式的,开放源码的分布式应用程序的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
- ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
- ZooKeeper代码版本中,提供了分布式独享锁、选举、队列的接口:其中分布锁和队列有Java和C两个版本,选举只有Java版本。
功能与特性
数据结构
zookeeper提供了类似Linux文件系统一样的数据结构。每一个节点对应一个Znode节点,每一个Znode节点都可以存储1MB(默认)的数据。客户端对zk的操作就是对Znode节点的操作。
znode图中的每个节点称为一个znode,znode 是被它所在的路径唯一标识:Server1 这个 znode 的标识为 /NameService/Server1, 每个znode由3部分组成(Znode:包含ACL权限控制、修改/访问时间、最后一次操作的事务Id(zxid)等等):
- stat. 此为状态信息, 描述该znode的版本, 权限等信息.
- data. 与该znode关联的数据.
- children. 该znode下的子节点.
数据结构特点
每一个Znode节点又根据节点的生命周期与类型分为4种节点。
结构特点- 生命周期:当客户端会话结束的时候,是否清理掉这个会话创建的节点。持久-不清理,临时-清理。
- 类型:每一个会话,创建单独的节点(例子:正常节点:app,顺序编号节点:app1,app2等等)
Zookeeper 这种数据结构有如下这些特点
-
每个子目录项如 NameService 都被称作为 znode,这个 znode 是被它所在的路径唯一标识,如 Server1 这个 znode 的标识为 /NameService/Server1
-
znode 可以有子节点目录,并且每个 znode 可以存储数据,注意 EPHEMERAL 类型的目录节点不能有子节点目录
-
znode 是有版本的,每个 znode 中存储的数据可以有多个版本,也就是一个访问路径中可以存储多份数据
-
znode 可以是临时节点,一旦创建这个 znode 的客户端与服务器失去联系,这个 znode 也将自动删除,Zookeeper 的客户端和服务器通信采用长连接方式,每个客户端和服务器通过心跳来保持连接,这个连接状态称为 session,如果 znode 是临时节点,这个 session 失效,znode 也就删除了
-
znode 的目录名可以自动编号,如 App1 已经存在,再创建的话,将会自动命名为 App2
-
znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个是 Zookeeper 的核心特性,Zookeeper 的很多功能都是基于这个特性实现的,后面在典型的应用场景中会有实例介绍
监听机制
Zookeeper 这种数据结构特点第七条提及znode 可以被监控,即zookeeper除了提供对Znode节点的处理能力,还提供了对节点的变更进行监听通知的能力。
监听机制的步骤如下:
- 任何session(session1,session2)都可以对自己感兴趣的znode监听。
- 当znode通过session1对节点进行了修改。
- session1,session2都会收到znode的变更事件通知。
节点常见的事件通知有:
- session建立成功事件
- 节点添加
- 节点删除
- 节点变更
- 子节点列表变化
一次监听事件,只会被触发一次,如果想要监听到znode的第二次变更,需要重新注册监听。
使用场景
注册中心
zookeeper用得比较多的地方可能是,微服务的集群管理与服务注册与发现。
- 依赖于临时节点
- 消费者启动的时候,会先去注册中心中全量拉取服务的注册列表。
- 当某个服务节点有变化的时候,通过监听机制做数据更新。
- zookeeper挂了,不影响消费者的服务调用。
分布式锁
zookeeper-分布式锁- 依赖于临时顺序节点
- 判断当前client的顺序号是否是最小的,如果是获取到锁。
- 没有获取到锁的节点监听最小节点的删除事件(比如lock_key_001)
- 锁释放,最小节点删除,剩余节点重新开始获取锁。
- 重复步骤二到四。
高性能高可用强一致性保障
zookeeper-集群高性能,我们通常想到的是通过集群部署来突破单机的性能瓶颈。对于zk来说,就是通过部署多个节点共同对外提供服务,来提供读的高性能。
- Master/Slave模式。
- 在zookeeper中部署多台节点对外提供服务,客户端可以连接到任意一个节点。
- 每个节点的数据都是一样的。
- 节点根据角色分为Leader节点与Learner节点(包括Follower节点与Observer节点)。
- 集群中,只有一个Leader节点,完成所有的写请求处理。
- 每次写请求都会生成一个全局的唯一的64位整型的事务ID(可以理解为全局的数据的版本号)。
- Learner节点可以有很多,每个Leaner可以独自处理读请求,转写请求到Leader节点。
- 当Leader节点挂掉后,会从Follower节点中通过选举方式选出一个Leader提供对外服务。
- Follower节点与Observer节点区别在于不参与选举和提议的事务过半处理。
- 集群通常是按照奇数个节点进行部署(偶然太对容灾没啥影响,浪费机器)。
数据一致性(zab协议-原子广播协议)
通过集群的部署,根据CAP原理,这样,可能导致同一个数据在不同节点上的数据不一致。zookeeper通过zab原子广播协议来保证数据在每一个节点上的一致性。原子广播协议(类似2PC提交协议)大概分为3个步骤。
zab协议-原子广播协议- Leader包装写请求,生成唯一zxid,发起提议,广播给所有Follower。
- Follower收到提议后,写入本地事务日志,根据自身情况,是否同意该事务的提交。
- Leader收到过半的Follower同意,自己先添加事务。然后对所有的Learner节点发送提交事务请求。
需要说明的是,zookeeper对数据一致性的要求是:
- 顺序一致性:严格按照事务发起的顺序执行写操作。
- 原子性:所有事务请求的结果在集群中的所有节点上的应用情况是一致的。
- 单一视图:客户端访问任何一个节点,看到的数据模型都是一致的。
- 实时性:保证在极小一段时间客户端最终可以从服务读取最新数据状态(如果要实时,需要客户端调用syn方法)。
可用性-leader选举(zab协议-崩溃恢复协议)
leader选举当集群初始化或Follower无法联系上Leader节点的时候,每个Follower开始进入选举模式。选举步骤如下:
- Follower节点第一次投票先投自己,然后将自己的选票广播给剩余的Follower节点。
- Follower节点接收到其他的选票。
- 选票比较:比较自己的与接收的选票的投票更有。
- 如果资金的选票不是最优选票,变更自己的选票,投最优选票的节点。
- 统计自己收到的选票,如果某个节点获得了过半的节点的投票。确认该节点为新的Leader节点。
- 确认Leader节点后,每个节点变更自己的角色。完成投票选举。
- 选举原则:谁的数据最新,谁就有优先被选为Leader的资格。
举个例子,假如现在zk集群有5个节点,然后挂掉了2个节点。剩余节点S3,S4,S6开始进行选举,他们的最大事务ID分别是6,2,6。定义投票结构为(投票的节点ID,被投节点ID,被投节点最大事务ID)。
leader选举- 初始状态,S3,S4,S5分别投自己,并带上自己的最大事务ID。
- S3,S4,S5分别对自己收到的2票与自己的1票做比较。
- S5发现自己的是最优投票,不变更投票,S3,S4发现S5的投票是最优解,更改投票。
- S3,S4广播自己变更的投票。
- 最后大家都确认了S5是Leader,S5节点状态变更为Leader节点,S3,S4变更为Follower节点。
数据的持久化
数据的持久化- zookeeper所有数据都存在内存中。
- zookeeper会定期将内存dump到磁盘中,形成数据快照。
- zookeeper每次的事务请求,都会先接入到磁盘中,形成事务日志。
- 全量数据 = 数据快照 + 事务日志。
网友评论