zookeeper基础概念
1.1 ZooKeeper 概述
**Zookeeper 是一个分布式协调服务的开源框架。**主要用来解决分布式集群中应用系统的一致性问题,例如怎样避免同时操作同一数据造成脏读的问题。
**ZooKeeper 本质上是一个分布式的小文件存储系统。**提供基于类似于文件系统的目录树方式的数据存储,并且可以对树中的节点进行有效管理。从而用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。诸如:统一命名服务、分布式配置管理、分布式消息队列、分布式锁、分布式协调等功能。
分布式协调服务: 就是可以在分布式配置中共享配置,协调锁资源,提供命名服务等。
1.2 ZooKeeper 特性
- 全局数据一致:★★★ 每个 server 保存一份相同的数据副本,client 无论连接到哪个 server,展示的数据都是一致的,这是最重要的特征;
- 可靠性:如果消息被其中一台服务器接受,那么将被所有的服务器接受。
- 顺序性:包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息 a 在消息 b 前发布,则在所有 Server 上消息 a 都将在消息 b 前被发布;偏序是指如果一个消息 b 在消息 a 后被同一个发送者发布,a 必将排在 b 前面。
- 数据更新原子性:一次数据更新要么成功(半数以上节点成功),要么失败,不存在中间状态;
- 实时性:Zookeeper 保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。不会超过几十秒。
- 单一系统映像: 一个客户端无论连接到那一台服务器,他看到的都是同样的系统试图。意思就是,如果客户端无论因为什么,在同一个会话中连接到了一台新的服务器,它所看到的数据都不会比之前的服务器看到的更老。当一台服务器出现问题时,导致他的客户端需要连接一台新的服务器时,所有状态滞后与故障服务器的服务器都不会接受该连接请求,除非这些服务器将状态跟新值故障服务器的水平。
- 持久性: 一旦对zookeeper操作成功,其结果就会持久存在,不会被撤销。
跨客户端试图的同时一致性: 由于性能原因,可能客户端A将znode的值从 a
更新为 b
,通知客户端B去获取,然后B客户端读到的数据却是 a
,不是 b
,这与zookeeper的一致性保证是完全兼容的。
解决: 为了避免这种现象发生,客户端B应该在都读取前调用sync操作,将当前连接的服务器与leader的数据同步。
1.3 ZooKeeper 集群角色
Leader:
Zookeeper 集群工作的核心
事务请求(写操作)的唯一调度和处理者,保证集群事务处理的顺序性;集群内部各个服务器的调度者。
对于 create,setData,delete 等有写操作的请求,则需要统一转发给leader 处理,leader 需要决定编号、执行操作,这个过程称为一个事务。
可以通过将 leaderServes
属性设置为no来实现是领导者不接受任何客户端的连接,在这种情况下,领导者的唯一任务就是协调更新。
Follower:
处理客户端非事务(读操作)请求,转发事务请求给 Leader;
参与集群 Leader 选举投票。
此外,针对访问量比较大的 zookeeper 集群,还可新增观察者角色。
Observer:
观察者角色,观察 Zookeeper 集群的最新状态变化并将这些状态同步过来,其对于非事务请求可以进行独立处理,对于事务请求,则会转发给 Leader服务器进行处理。
不会参与任何形式的投票只提供非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力。
1.4 ZooKeeper 数据模型
ZooKeeper 的数据模型,在结构上和标准文件系统的非常相似,拥有一个层次的命名空间,都是采用树形层次结构,ZooKeeper 树中的每个节点被称为—Znode。和文件系统的目录树一样ZooKeeper 树中的每个节点可以拥有子节点。
但也有不同之处:
- **Znode 兼具文件和目录两种特点。**既像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分,并可以具有子 Znode。用户对 Znode 具有增、删、改、查等操作(权限允许的情况下)。
- **Znode 具有原子性操作,读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。**另外,每一个节点都拥有自己的 ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的操作。
- **Znode 存储数据大小有限制。**ZooKeeper 虽然可以关联一些数据,但并没有被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,比如分布式应用中的配置文件信息、状态信息、汇集位置等等。这些数据的共同特性就是它们都是很小的数据,通常以 KB 为大小单位。ZooKeeper 的服务器和客户端都被设计为严格检查并限制每个 Znode 的数据大小至多 1M,当时常规使用中应该远小于此值。
- **Znode 通过路径引用,如同 Unix 中的文件路径。**路径必须是绝对的(绝对路径),因此他们必须由斜杠字符来开头。除此以外,他们必须是唯一的,也就是说每一个路径只有一个表示,因此这些路径不能改变。在 ZooKeeper 中,路径由Unicode 字符串组成,并且有一些限制。字符串"/zookeeper"用以保存管理信息,比如关键配额信息。
所有的路径表示必须是规范的,机每条路径只有唯一的一种表示方式,不支持路径解析。
在zookeeper中,不能使用 “.” 来代表当前路径下。
图中的每个节点称为一个 Znode。 每个 Znode 由 4 部分组成:
- stat:此为状态信息, 描述该 Znode 的版本, 权限等信息
- data:与该 Znode 关联的数据
- children:该 Znode 下的子节点
- ACL:记录Znode的访问权限,即哪些人或哪些IP可以访问本节点。
1.5 节点属性
每个 znode 都包含了一系列的属性,通过命令 get 节点路径
,可以获得节点的属性。
- **dataVersion:**数据版本号,每次对节点进行 set 操作,dataVersion 的值都会增加 1(即使设置的是相同的数据),可有效避免了数据更新时出现的先后顺序问题。
- **cversion :**子节点的版本号。当 znode 的子节点有变化时,cversion 的值就会增加 1。
- **aclVersion :**ACL 的版本号。
- **cZxid :**Znode 创建的事务 id。
- **mZxid :**Znode 被修改的事务 id,即每次对 znode 的修改都会更新 mZxid。对于 zk 来说,每次的变化都会产生一个唯一的事务 id,zxid(ZooKeeper Transaction Id)。通过 zxid,可以确定更新操作的先后顺序。例如,如果 zxid1小于 zxid2,说明 zxid1 操作先于 zxid2 发生,zxid 对于整个 zk 都是唯一的,即使操作的是不同的 znode。
- **ctime:**节点创建时的时间戳.
- **mtime:**节点最新一次更新发生时的时间戳.
- **ephemeralOwner:**如果该节点为临时节点, ephemeralOwner 值表示与该节点绑定的 session id. 如果不是, ephemeralOwner 值为 0.
在 client 和 server 通信之前,首先需要建立连接,该连接称为 session。连接建立后,如果发生连接超时、授权失败,或者显式关闭连接,连接便处于 CLOSED状态, 此时 session 结束
1.6 节点类型
Znode 有两种,分别为临时节点和永久节点。节点的类型在创建时即被确定,并且不能改变。
- 临时节点:该节点的生命周期依赖于创建它们的会话。一旦会话结束,临时节点将被自动删除,当然可以也可以手动删除。临时节点不允许拥有子节点。
- **永久节点:**该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。
Znode 还有一个序列化的特性,如果创建的时候指定的话,该 Znode 的名字后面会自动追加一个不断增加的序列号。序列号对于此节点的父节点来说是唯一的,这样便会记录每个子节点创建的先后顺序。它的格式为“%10d”(10 位数字,没有数值的数位用 0 补充,例如“0000000001”)。
这样便会存在四种类型的 Znode 节点,分别对应:
- PERSISTENT:永久节点
- EPHEMERAL:临时节点
- PERSISTENT_SEQUENTIAL:永久节点、序列化
- EPHEMERAL_SEQUENTIAL:临时节点、序列化
ZooKeeper中的时间
ZooKeeper有多种记录时间的形式,其中包含以下几个主要属性:
- Zxid
致使ZooKeeper节点状态改变的每一个操作都将使节点接收到一个zxid格式的时间戳,并且这个时间戳全局有序。也就是说,也就是说,每个对节点的改变都将产生一个唯一的zxid。如果zxid1的值小于zxid2的值,那么zxid1所对应的事件发生在zxid2所对应的事件之前。实际上,ZooKeeper的每个节点维护者三个zxid值,为别为:cZxid、mZxid、pZxid。 - cZxid: 是节点的创建时间所对应的Zxid格式时间戳。
- **mZxid:**是节点的修改时间所对应的Zxid格式时间戳。
实现中zxid是一个64为的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个 新的epoch。低32位是个递增计数。