1.Zookeeper是什么
ZooKeeper=文件系统+监听通知
它是一个分布式服务协调框架,提供了分布式数据一致性的解决方案,基于ZooKeeper的数据结构,Zookeeper Atomic Broadcast(ZAB,Zookeeper原子消息广播协议),Watcher,选举机制等特点,可以实现数据的发布/订阅,软负载均衡,命名服务,统一配置管理,分布式锁,集群管理等等。
2.为什么使用Zookeeper
ZooKeeper能保证:
- 更新请求顺序进行。来自同一个client的更新请求按其发送顺序依次执行
- 数据更新原子性。一次数据更新要么成功,要么失败
- 全局唯一数据视图。client无论连接到哪个server,数据视图都是一致的
- 实时性。在一定时间范围内,client读到的数据是最新的
3.数据结构
ZooKeeper的数据结构和Unix文件系统很类似,总体上可以看做是一棵树,每一个节点称之为一个ZNode,每一个ZNode默认能存储1M的数据。每一个ZNode可通过唯一的路径标识。如下图所示:
创建ZNode时,可以指定以下四种类型,包括:
- PERSISTENT,持久性ZNode。创建后,即使客户端与服务端断开连接也不会删除,只有客户端主动删除才会消失。
- PERSISTENT_SEQUENTIAL,持久性顺序编号ZNode。和持久性节点一样不会因为断开连接后而删除,并且ZNode的编号会自动增加。
- EPHEMERAL,临时性ZNode。客户端与服务端断开连接,该ZNode会被删除。
- EPEMERAL_SEQUENTIAL,临时性顺序编号ZNode。和临时性节点一样,断开连接会被删除,并且ZNode的编号会自动增加。
ZooKeeper 数据模型采用层次化的多叉树形结构,每个节点上都可以存储数据,这些数据可以是数字、字符串或者是二级制序列。并且。每个节点还可以拥有 N 个子节点,最上层是根节点以“/”来代表。每个数据节点在 ZooKeeper 中被称为 znode,它是 ZooKeeper 中数据的最小单元。并且,每个 znode 都一个唯一的路径标识。
强调一句:ZooKeeper 主要是用来协调服务的,而不是用来存储业务数据的,所以不要放比较大的数据在 znode 上,ZooKeeper 给出的上限是每个结点的数据大小最大是 1M。
4.监听通知机制
Watcher是基于观察者模式实现的一种机制。如果我们需要实现当某个ZNode节点发生变化时收到通知,就可以使用Watcher监听器。
客户端通过设置监视点(watcher)向 ZooKeeper 注册需要接收通知的 znode,在 znode 发生变化时 ZooKeeper 就会向客户端发送消息。
这种通知机制是一次性的。一旦watcher被触发,ZooKeeper就会从相应的存储中删除。如果需要不断监听ZNode的变化,可以在收到通知后再设置新的watcher注册到ZooKeeper。
监视点的类型有很多,如监控ZNode数据变化、监控ZNode子节点变化、监控ZNode 创建或删除。
5.选举机制
ZooKeeper是一个高可用的应用框架,因为ZooKeeper是支持集群的。ZooKeeper在集群状态下,配置文件是不会指定Master和Slave,而是在ZooKeeper服务器初始化时就在内部进行选举,产生一台做为Leader,多台做为Follower,并且遵守半数可用原则。
由于遵守半数可用原则,所以5台服务器和6台服务器,实际上最大允许宕机数量都是3台,所以为了节约成本,集群的服务器数量一般设置为奇数。
如果在运行时,如果长时间无法和Leader保持连接的话,则会再次进行选举,产生新的Leader,以保证服务的可用
zookeeper 的 leader 选举存在两个阶段,一个是服务器启动时 leader 选举,另一个是运行过程中 leader 服务器宕机。在分析选举原理前,先介绍几个重要的参数。
- 服务器 ID(myid):编号越大在选举算法中权重越大
- 事务 ID(zxid):值越大说明数据越新,权重越大
- 逻辑时钟(epoch-logicalclock):同一轮投票过程中的逻辑时钟值是相同的,每投完一次值会增加
zookeeper中三种角色
| Leader | 为客户端提供读和写的服务,负责投票的发起和决议,更新系统状态 |
|---|---|
| Follower(Learner) | 为客户端提供读服务,如果是写服务则转发给 Leader。在选举过程中参与投票。 |
| Observer(Learner) | 为客户端提供读服务器,如果是写服务则转发给 Leader。不参与选举过程中的投票,也不参与“过半写成功”策略 |
当 Leader 服务器出现网络中断、崩溃退出与重启等异常情况时,就会进入 Leader 选举过程,这个过程会选举产生新的 Leader 服务器。
这个过程大致是这样的:
- Leader election(选举阶段) :节点在一开始都处于选举阶段,只要有一个节点得到超半数节点的票数,它就可以当选准 leader。
- Discovery(发现阶段) :在这个阶段,followers 跟准 leader 进行通信,同步 followers 最近接收的事务提议。
- Synchronization(同步阶段) :同步阶段主要是利用 leader 前一阶段获得的最新提议历史,同步集群中所有的副本。同步完成之后 准 leader 才会成为真正的 leader。
- Broadcast(广播阶段) :到了这个阶段,ZooKeeper 集群才能正式对外提供事务服务,并且 leader 可以进行消息广播。同时如果有新的节点加入,还需要对新节点进行同步。
6.集群管理
ZooKeeper 集群中的服务器状态
- LOOKING :寻找 Leader。
- LEADING :Leader 状态,对应的节点为 Leader。
- FOLLOWING :Follower 状态,对应的节点为 Follower。
- OBSERVING :Observer 状态,对应节点为 Observer,该节点不参与 Leader 选举。
7.数据同步
在 Zookeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性。
ZAB 协议分为两部分:
- 消息广播
- 崩溃恢复
消息广播
Zookeeper 使用单一的主进程 Leader 来接收和处理客户端所有事务请求,并采用 ZAB 协议的原子广播协议,将事务请求以 Proposal 提议广播到所有 Follower 节点,当集群中有过半的Follower 服务器进行正确的 ACK 反馈,那么Leader就会再次向所有的 Follower 服务器发送commit 消息,将此次提案进行提交。这个过程可以简称为 2pc 事务提交,整个流程可以参考下图,注意 Observer 节点只负责同步 Leader 数据,不参与 2PC 数据同步过程。
崩溃恢复
在正常情况消息广播情况下能运行良好,但是一旦 Leader 服务器出现崩溃,或者由于网络原理导致 Leader 服务器失去了与过半 Follower 的通信,那么就会进入崩溃恢复模式,需要选举出一个新的 Leader 服务器。在这个过程中可能会出现两种数据不一致性的隐患,需要 ZAB 协议的特性进行避免。
- 1、Leader 服务器将消息 commit 发出后,立即崩溃
- 2、Leader 服务器刚提出 proposal 后,立即崩溃
ZAB 协议的恢复模式使用了以下策略:
- 1、选举 zxid 最大的节点作为新的 leader
- 2、新 leader 将事务日志中尚未提交的消息进行处理
8.权限管理
zookeeper 的 ACL(Access Control List,访问控制表)权限在生产环境是特别重要的,所以本章节特别介绍一下。
ACL 权限可以针对节点设置相关读写等权限,保障数据安全性。
permissions 可以指定不同的权限范围及角色。zookeeper 的 acl 通过 [scheme🆔permissions] 来构成权限列表。
- 1、scheme:代表采用的某种权限机制,包括 world、auth、digest、ip、super 几种。
- 2、id:代表允许访问的用户。
- 3、permissions:权限组合字符串,由 cdrwa 组成,其中每个字母代表支持不同权限, 创建权限 create(c)、删除权限 delete(d)、读权限 read(r)、写权限 write(w)、管理权限admin(a)。
| cZxid | 创建节点时的事务ID |
|---|---|
| ctime | 创建节点时的时间 |
| mZxid | 最后修改节点时的事务ID |
| mtime | 最后修改节点时的时间 |
| pZxid | 表示该节点的子节点列表最后一次修改的事务ID,添加子节点或删除子节点就会影响子节点列表,但是修改子节点的数据内容则不影响该ID (注意,只有子节点列表变更了才会变更pzxid,子节点内容变更不会影响pzxid) |
| cversion | 子节点版本号,子节点每次修改版本号加1 |
| dataversion | 数据版本号,数据每次修改该版本号加1 |
| aclversion | 权限版本号,权限每次修改该版本号加1 |
| ephemeralOwner | 创建该临时节点的会话的sessionID。 (** 如果该节点是持久节点,那么这个属性值为0)** |
| dataLength | 该节点的数据长度 |
| numChildren | 该节点拥有子节点的数量 (只统计直接子节点的数量) |
9.分布式锁
排他锁
排他锁(Exclusive Locks),又被称为写锁或独占锁,如果事务T1对数据对象O1加上排他锁,那么整个加锁期间,只允许事务T1对O1进行读取和更新操作,其他任何事务都不能进行读或写。
定义锁:
/exclusive_lock/lock
实现方式:
利用 zookeeper 的同级节点的唯一性特性,在需要获取排他锁时,所有的客户端试图通过调用 create() 接口,在 /exclusive_lock 节点下创建临时子节点 /exclusive_lock/lock,最终只有一个客户端能创建成功,那么此客户端就获得了分布式锁。同时,所有没有获取到锁的客户端可以在 /exclusive_lock 节点上注册一个子节点变更的 watcher 监听事件,以便重新争取获得锁。
共享锁
共享锁(Shared Locks),又称读锁。如果事务T1对数据对象O1加上了共享锁,那么当前事务只能对O1进行读取操作,其他事务也只能对这个数据对象加共享锁,直到该数据对象上的所有共享锁都释放。
定义锁:
/shared_lock/[hostname]-请求类型W/R-序号
实现方式:
1、客户端调用 create 方法创建类似定义锁方式的临时顺序节点。
2、客户端调用 getChildren 接口来获取所有已创建的子节点列表。
3、判断是否获得锁,对于读请求如果所有比自己小的子节点都是读请求或者没有比自己序号小的子节点,表明已经成功获取共享锁,同时开始执行度逻辑。对于写请求,如果自己不是序号最小的子节点,那么就进入等待。
4、如果没有获取到共享锁,读请求向比自己序号小的最后一个写请求节点注册 watcher 监听,写请求向比自己序号小的最后一个节点注册watcher 监听。
8.四字命令
| conf | 3.3.0版本引入的。打印出服务相关配置的详细信息。 |
|---|---|
| cons | 3.3.0版本引入的。列出所有连接到这台服务器的客户端全部连接/会话详细信息。包括"接受/发送"的包数量、会话id、操作延迟、最后的操作执行等等信息。 |
| crst | 3.3.0版本引入的。重置所有连接的连接和会话统计信息。 |
| dump | 列出那些比较重要的会话和临时节点。这个命令只能在leader节点上有用。 |
| envi | 打印出服务环境的详细信息。 |
| reqs | 列出未经处理的请求 |
| ruok | 测试服务是否处于正确状态。如果确实如此,那么服务返回"imok",否则不做任何相应。 |
| stat | 输出关于性能和连接的客户端的列表。 |
| srst | 重置服务器的统计。 |
| srvr | 3.3.0版本引入的。列出连接服务器的详细信息 |
| wchs | 3.3.0版本引入的。列出服务器watch的详细信息。 |
| wchc | 3.3.0版本引入的。通过session列出服务器watch的详细信息,它的输出是一个与watch相关的会话的列表。 |
| wchp | 3.3.0版本引入的。通过路径列出服务器watch的详细信息。它输出一个与session相关的路径。 |
| mntr | 3.4.0版本引入的。输出可用于检测集群健康状态的变量列表 |
znode(数据节点)
介绍了 ZooKeeper 树形数据模型之后,我们知道每个数据节点在 ZooKeeper 中被称为 znode,它是 ZooKeeper 中数据的最小单元。你要存放的数据就放在上面,是你使用 ZooKeeper 过程中经常需要接触到的一个概念。
我们通常是将 znode 分为 4 大类:
- 持久(PERSISTENT)节点 :一旦创建就一直存在即使 ZooKeeper 集群宕机,直到将其删除。
- 临时(EPHEMERAL)节点 :临时节点的生命周期是与 客户端会话(session) 绑定的,会话消失则节点消失 。并且,临时节点只能做叶子节点 ,不能创建子节点。
- 持久顺序(PERSISTENT_SEQUENTIAL)节点 :除了具有持久(PERSISTENT)节点的特性之外, 子节点的名称还具有顺序性。比如
/node1/app0000000001、/node1/app0000000002。 - 临时顺序(EPHEMERAL_SEQUENTIAL)节点 :除了具备临时(EPHEMERAL)节点的特性之外,子节点的名称还具有顺序性。
每个 znode 由 2 部分组成:
- stat :状态信息
- data : 节点存放的数据的具体内容
7.CAP理论
一致性:Consistency,数据在多个副本之间能够保持一致的特性。
可用性:Availability,系统提供的服务必须一致处于可用的状态,每次请求都能获取到非错的响应。
分区容错性:Partition tolerance,分布式系统在遇到任何网络分区故障的时候,任然能够对外提供满 足一致性和可用性的服务。
三选二
在现实环境中必然存在通信失败的情况(分区容错),可用性和一致性之间存在矛盾。
- 在保证一致性情况下,ClientA端向ServerA写入数据时,ClientB从ServerB读取数据,要保证数据 的一致性,就必须在ServerA写的同时将ServerB的读写锁住,ServerA的数据同步更新到ServerB 之后释放,和可用性矛盾
- 要保证可用性,那就不能锁定ServerB,和一致性矛盾
在不要求分区容错的情况下,中心化服务,数据不存在分区
因此无法同时保证一致性和可用性,CAP中只能选其二
CP:牺牲可用性,出现网络故障或者消息丢失会影响用户体验,保证数据的绝对一致。Redis、HBase 设计遵循CP原则
AP:牺牲一致性(或者说是绝对一致),当不同的分区之间网络故障,使用本地数据提供服务
CA:牺牲分区容错,违反了分布式设计初衷,无法扩展子节点,可以理解成数据集中在一个中心。传统 的关系型数据库RDBMS、ORACLE、MySQL遵循CA原则
BASE
BASE理论是对CAP中一致性和可用的权衡结果,基于CAP原则演化而来,核心思想就是无法做到强一致 性(Strong Consistency),但是业务可以根据自身的特点,采用适当的方式做到最终一致。
三要素
- Base Available 基本可用,当分布式系统出现不可以预知的故障时,运行损失部分的可用性,例如:响应时间、功 能损失。但是不等于系统不可用
- Soft State 软状态,允许数据存在中间状态,并且总监状态不会影响系统的可用性
- Eventually Consistent 最终一致,系统中的所有副本在经过一段时间的同步之后能一致。
最终一致的几个变种
- 因果一致 数据的写入是按照一定的因果关系,读取这些数据的时候也会按照该因果关系出现
- 读己之所写 每次读取的数据不会比自己上一次写入的数据旧
- 会话一致性 在会话内部保证读己之所写一致性,只要会话存在就能保证
- 单调读一致性 先前读取到较新的数据之后不会得到更旧的数据
- 单调写一致性 保证同一个进程写操作的执行顺序
2PC&3PC
2PC:2-Phase Commit
-
提交事务请求(Proposal)
-
执行事务(Commit)
-
正常提交
-
中断事务(失败回滚)
-
3PC:3-Phase Commit
总结
- ZooKeeper 本身就是一个分布式程序(只要半数以上节点存活,ZooKeeper 就能正常服务)。
- 为了保证高可用,最好是以集群形态来部署 ZooKeeper,这样只要集群中大部分机器是可用的(能够容忍一定的机器故障),那么 ZooKeeper 本身仍然是可用的。
- ZooKeeper 将数据保存在内存中,这也就保证了 高吞吐量和低延迟(但是内存限制了能够存储的容量不太大,此限制也是保持 znode 中存储的数据量较小的进一步原因)。
- ZooKeeper 是高性能的。 在“读”多于“写”的应用程序中尤其地明显,因为“写”会导致所有的服务器间同步状态。(“读”多于“写”是协调服务的典型场景。)
- ZooKeeper 有临时节点的概念。 当创建临时节点的客户端会话一直保持活动,瞬时节点就一直存在。而当会话终结时,瞬时节点被删除。持久节点是指一旦这个 znode 被创建了,除非主动进行 znode 的移除操作,否则这个 znode 将一直保存在 ZooKeeper 上。
- ZooKeeper 底层其实只提供了两个功能:① 管理(存储、读取)用户程序提交的数据;② 为用户程序提供数据节点监听服务。
命令
服务管理
zkServer.sh start/stop/restart
客户端
./zkCli.sh -server ip:port
节点管理
创建节点
使用create命令,可以创建一个Zookeeper节点, 如
create [-s] [-e] path data acl
-s 顺序节点 -e 临时节点 没有参数 永久节点
读取节点
ls path [-w]
get path [-w]
更新节点
set path data [version]
data就是要更新的新内容,version表示数据版本,如将/zk-permanent节点的数据更新为456,可以使用如下命令:set /zk-permanent 456
删除节点
delete path -v