本质
分布式协调者,zookeeper= 文件系统+通知机制
可以提供的服务有:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等
文件系统
Zookeeper维护一个类似文件系统的数据结构
每个子目录项如 NameService 都被称作为 znode,和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,唯一的不同在于znode是可以存储数据的。而文件系统中只有文件节点可以存放数据而目录节点不行。
ZooKeeper 为了保证高吞吐和低延迟,在内存中维护了这个树状的目录结构,这种特性使得 ZooKeeper 不能用于存放大量的数据,每个节点的存放数据上限为1M
有四种类型的znode:
1、PERSISTENT-持久化目录节点
客户端与zookeeper断开连接后,该节点依旧存在
2、 PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点
客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
3、EPHEMERAL-临时目录节点
客户端与zookeeper断开连接后,该节点被删除
4、EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点
客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
架构
-
leader,为客户端提供读写功能。在选举中负责投票的发起和决议。
-
follower,为客户单提供读服务,写请求转发给leader。在选举中进行投票。
-
observer,为客户端提供读服务,写请求转发leader。不参与一致性协议过半写入和选举机制,只为提高读性能。
根据以上架构可得,服务器具有四种状态,分别是 Looking、Following、Leading、Observing
(1) Looking:寻找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。
(2)Following:跟随者状态。表明当前服务器角色是 Follower。
(3)Leading:领导者状态。表明当前服务器角色是 Leader。
(4)Observing:观察者状态。表明当前服务器角色是 Observer。
ZAP协议
ZooKeeper 的核心是原子广播机制,这个机制保证了各个 Server 之间的同步。实现这个机制的协议叫做 Zab 协议。Zab 协议有两种模式,它们分别是恢复模式和广播模式。
(1) 恢复模式
当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数 Server 完成了和 leader 的状态同步以后,恢复模式就结束了。状态同步保证了 Leader 和 Server 具有相同的系统状态。
(2)广播模式
一旦 Leader 已经和多数的 Follower 进行了状态同步后,它就可以开始广播消息了,即进入广播状态。这时候当一个 Server 加入 ZooKeeper 服务中,它会在恢复模式下启动,发现Leader,并和Leader 进行状态同步。待到同步结束,它也参与消息广播。ZooKeeper 服务一直维持在 Broadcast 状态,直到 Leader 崩溃了或者 Leader 失去了大部分的 Followers 支持。
CAP应用
无例外,zookeeper也遵循cap原则,保证cp追求a,一致性是zookeeper最必要的,p是必要的,所以只能追求a了,zab算法的过程如下
(1)ZooKeeper写数据都是leader节点,leader节点会把数据通过proposal请求发送到所有节点
(2)所有节点收到数据以后都会写到自己到本地磁盘上面,成功后发送一个ack请求给leader
(3)leader只要接受到过半的节点ack响应,就会发送commit消息给各个节点,各个节点就会把消息放入到内存中
(4)返回客户端写入成功
Paxos 和 ZAP
相同点:
(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
特征:
(1)一次性:无论是服务端还是客户端,一旦一个 Watcher 被触发 ,ZooKeeper 都会将其从相应的存储中移除。这样的设计有效的减轻了服务端的压力,不然对于更新非常频繁的节点,服务端会不断的向客户端发送事件通知,无论对于网络还是服务端的压力都非常大。
(2)客户端串行执行:客户端 Watcher 回调的过程是一个串行同步的过程。
(3)轻量:
-
Watcher 通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。
-
客户端向服务端注册 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 事件可能会被丢失。
例子:
官方声明:一个 Watch 事件是一个一次性的触发器,当被设置了 Watch的数据发生了改变的时候,则服务器将这个改变发送给设置了 Watch 的客户端,以便通知它们。
为什么不是永久的,举个例子,如果服务端变动频繁,而监听的客户端很多情况下,每次变动都要通知到所有的客户端,给网络和服务器造成很大压力。
一般是客户端执行 getData(“/节点 A”,true),如果节点 A 发生了变更或删除,客户端会得到它的 watch 事件,但是在之后节点 A 又发生了变更,而客户端又没有设置 watch 事件,就不再给客户端发送。
在实际应用中,很多情况下,我们的客户端不需要知道服务端的每一次变动,我只要最新的数据即可。
应用场景:
ZooKeeper 是一个典型的发布/订阅模式的分布式数据管理与协调框架,开发人员可以使用它来进行分布式数据的发布和订阅。
通过对 ZooKeeper 中丰富的数据节点进行交叉使用,配合 Watcher 事件通知机制,可以非常方便的构建一系列分布式应用中都会涉及的核心功能,如:
(1)数据发布/订阅
(2)负载均衡
(3)命名服务
(4)分布式协调/通知
(5)集群管理
(6)Master 选举
(7)分布式锁
(8)分布式队列数据发布/订阅介绍
数据发布/订阅系统
即所谓的配置中心,顾名思义就是发布者发布数据供订阅者进行数据订阅。目的动态获取数据(配置信息)实现数据(配置信息)的集中式管理和数据的动态更新
(1)数据量通常比较小
(2)数据内容在运行时会发生动态更新
(3)集群中各机器共享,配置一致
如:机器列表信息、运行时开关配置、数据库配置信息等
命名服务
zk 的命名服务命名服务是指通过指定的名字来获取资源或者服务的地址,利用 zk 创建一个全局的路径,这个路径就可以作为一个名字,指向集群中的集群,提供的服务的地址,或者一个远程的对象等等。
分布式通知和协调
对于系统调度来说:操作人员发送通知实际是通过控制台改变某个节点的状态,然后 zk 将这些变化发送给注册了这个节点的 watcher 的所有客户端。
对于执行情况汇报:每个工作进程都在某个目录下创建一个临时节点。并携带工作的进度数据,这样汇总的进程可以监控目录子节点的变化获得工作进度的实时的全局情况。
命名服务(文件系统)
分布式锁(文件系统、通知机制)
有了 ZooKeeper 的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一个是保持独占,另一个是控制时序。对于第一类,我们将 ZooKeeper 上的一个 Znode 看作是一把锁,通过 Createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创