Zookeeper概述

ZooKeeper:分布式协调技术,主要用来解决分布式环境当中多个进程之间的同步控制,让他们有序的去访问某种临界资源,防止造成"脏数据"的后果;分布式协调技术的核心就是来实现这个分布式锁;

zookeeper 常用功能

分布式协调技术的核心就是来实现这个分布式锁;

  • 服务注册与订阅(共用节点)
  • 分布式通知(监听znode)分布式配置中心
  • 服务命名(znode特性)
  • 数据订阅、发布(watcher)
  • 分布式锁(临时节点)

ZK性能

例如,在它的诞生地Yahoo公司,对于写占主导的工作负载来说,ZooKeeper的基准吞吐量已经超过每秒10000个操作;对于常规的以读为主导的工作负载来说,吞吐量更是高出了好几倍。

zk功能特点

Zookeeper采用的通信协议:Zab协议
Zookeeper的数据结构:数据结构——Znode
Zookeeper的通信机制:Watcher机制
Zookeeper节点类型:持久、临时、持久顺序、临时顺序、持久 TTL、持久顺序 TTL、容器

Zookeeper的通信机制:Watcher机制

客户端向zk服务器注册watcher的同时,会将watcher对象储在客户端的watchManager,Zk服务器器触发watcher事件后,会向客户端发送通知,客户端线程从watchManager中掉起watcher执行
ZooKeeper所提供的服务主要是通过:数据结构+原语+watcher机制
Watcher设置后,一旦触发一次即会失效,如果需要一直监听,就需要再次注册;

Zookeeper节点类型

  1. 持久、临时
    持久是用的最多的一种类型也是默认的节点类型,临时节点相较于持久节点来说就是它会随着客户端会话结束而被删除,通常可以用在一些特定的场景,如分布式锁释放、健康检查等。

  2. 持久顺序、临时顺序
    这两种放在一起介绍,他们相对于上面两种的特性就是ZK会自动在这两种节点之后增加一个数字的后缀,而路径 + 数字后缀是能保证唯一的,这数字后缀的应用场景可以实现诸如分布式队列,分布式公平锁等。

  3. 容器
    容器节点是 3.5 以后新增的节点类型,只要在调用 create 方法时指定 CreateMode 为 CONTAINER 即可创建容器的节点类型,容器节点的表现形式和持久节点是一样的,但是区别是 ZK 服务端启动后,会有一个单独的线程去扫描,所有的容器节点,当发现容器节点的子节点数量为 0 时,会自动删除该节点,除此之外和持久节点没有区别,官方注释给出的使用场景是 Container nodes are special purpose nodes useful for recipes such as leader, lock, etc. 说可以用在 leader 或者锁的场景中。

  4. 持久 TTL、持久顺序 TTL
    关于持久和顺序这两个关键字,不用我再解释了,这两种类型的节点重点是后面的 TTL,TTL 是 time to live 的缩写,指带有存活时间,简单来说就是当该节点下面没有子节点的话,超过了 TTL 指定时间后就会被自动删除,特性跟上面的容器节点很像,只是容器节点没有超时时间而已,但是 TTL 启用是需要额外的配置(这个之前也有提过)配置是 zookeeper.extendedTypesEnabled 需要配置成 true,否则的话创建 TTL 时会收到 Unimplemented 的报错。

ZooKeeper以Fast Paxos算法为基础,Paxos 算法存在活锁的问题,即当有多个proposer交错提交时有可能互相排斥导致没有一个proposer能提交成功,而Fast Paxos做了一些优化,通过选举产生一个领导者,只有leader才能提交proposer具体算法可见Fast Paxos。要想弄懂ZooKeeper首先得对Fast Paxos有所了解。

ZooKeeper目标是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。ZooKeeper包含一个简单的原语集提供Java和C的接口。

Zookeeper采用的通信协议:Zab协议

Zab协议规定:Zab 协议主要针对写请求,读请求直接读取Follower 、Observer角色,写请求经过Follower 请求投票经过leader 发送给Follower、Observer 角色; Client的所有写请求,都要转发给ZK服务中唯一的Server—Leader, 由Leader根据该请求发起一个Proposal。然后,其他的Server对该Proposal进行Vote。之后,Leader对Vote进行收 集,当t的写请求做出回应

ZooKeeper数据模型Znode

树形层次结构,ZooKeeper树中的每个节点被称为—Znode
(1) 引用方式:Zonde通过路径引用,如同Unix中的文件路径。路径必须是绝对的,因此他们必须由斜杠字符来开头,路径必须唯一。字符串"/zookeeper"用以保存管理信息,比如关键配额信息。
(2) Znode结构:
① stat:此为状态信息, 描述该Znode的版本, 权限等信息
② data:与该Znode关联的数据
③ children:该Znode下的子节点
配置文件信息、状态信息、汇集位置等等。这些数据的共同特性就是它们都是很小的数据,通常以KB为大小单位

广播模式

Zookeeper概述_第1张图片
广播模式类似一个简单的两阶段提交:Leader发起一个请求,收集选票,并且最终提交,图3.3演示了我们协议的消息流程。我们可以简化该两阶段提交协议,因为我们并没有"aborts"的情况。followers要么确认Leader的Propose,要么丢弃该Leader的Propose。没有"aborts"意味着,只要有指定数量的机器确认了该Propose,而不是等待所有机器的回应。
广播协议在所有的通讯过程中使用TCP的FIFO信道,通过使用该信道,使保持有序性变得非常的容易。通过FIFO信道,消息被有序的deliver。只要收到的消息一被处理,其顺序就会被保存下来。
Leader会广播已经被deliver的Proposal消息。在发出一个Proposal消息前,Leader会分配给Proposal一个单调递增的唯一id,称之为zxid。因为Zab保证了因果有序, 所以递交的消息也会按照zxid进行排序。广播是把Proposal封装到消息当中,并添加到指向Follower的输出队列中,通过FIFO信道发送到 Follower。当Follower收到一个Proposal(提议)时,会将其写入到磁盘,可以的话进行批量写入。一旦被写入到磁盘媒介当 中,Follower就会发送一个ACK给Leader。 当Leader收到了指定数量的ACK时,Leader将广播commit消息并在本地deliver(发送)该消息。当收到Leader发来commit消息 时,Follower也会递交该消息。
需要注意的是, 该简化的两阶段提交自身并不能解决Leader故障,所以我们 添加恢复模式来解决Leader故障。

恢复模式(分布式系统的单点故障)

(1) 恢复阶段概述
正常工作时Zab协议会一直处于广播模式,直到Leader故障或失去了指定数量的Followers。 为了保证进度,恢复过程中必须选举出一个新Leader,并且最终让所有的Server拥有一个正确的状态。对于Leader选举,需要一个能够成功高几 率的保证存活的算法。Leader选举协议,不仅能够让一个Leader得知它是leader,并且有指定数量的Follower同意该决定。如果 Leader选举阶段发生错误,那么Servers将不会取得进展。最终会发生超时,重新进行Leader选举。在我们的实现中,Leader选举有两种不同的实现方式。如果有指定数量的Server正常运行,快速选举的完成只需要几百毫秒。
(2)恢复阶段的保证
该恢复过程的复杂部分是在一个给定的时间内,提议冲突的绝对数量。最大数量冲突提议是一个可配置的选项,但是默认是1000。为了使该协议能够即使在Leader故障的情况下也能正常运作。我们需要做出两条具体的保证:
① 我们绝不能遗忘已经被deliver的消息,若一条消息在一台机器上被deliver,那么该消息必须将在每台机器上deliver。
② 我们必须丢弃已经被skip的消息。

ZooKeeper伸缩性

ZooKeeper角色类型

Zookeeper概述_第2张图片
ZooKeeper集群当中有两种角色Leader和Follower。Leader可以接受client 请求,也接收其他Server转发的写请求,负责更新系统状态。
 Follower也可以接收client请求,如果是写请求将转发给Leader来更新系统状态,读请求则由Follower的内存数据库直接响应。 ZooKeeper集群如图1.1所示。
但在ZooKeeper的3.3.3版本以后,ZooKeeper中又添加了一种新角色Observer。
Observer的作用同Follower类似,唯一区别就是它不参与选主过程。那么,我们就可以根据该特性将ZK集群中的Server分为两种:
(1) 投票Server:Leader、Follower
(2) 非投票Server:Observer

为什么引入Observer角色

其实在ZooKeeper中引入Observer,主要是为了使 ZooKeeper具有更好的可伸缩性。那么,何为可伸缩性?关于伸缩性,对于不同的人意味着不同的事情。 而在这里是说,如果我们的工作负载可以通过给系统分配更多的资源来分担,那么这个系统就是可伸缩的;一个不可伸缩的系统却无法通过增加资源来提升性能,甚 至会在工作负载增加时,性能会急剧下降。
在Observer出现以前,ZooKeeper的伸缩性由Follower来实现,我们可以通过添加Follower节点的数量来保证 ZooKeeper服务的读性能。但是随着Follower节点数量的增加,ZooKeeper服务的写性能受到了影响。为什么会出现这种情况?在此,我 们需要首先了解一下这个"ZK服务"是如何工作的。

ZooKeeper服务中的每个Server可服务于多个Client,并且Client可连接到ZK服务中的任一台Server来提交请求。若是读请求,则由每台Server的本地副本数据库直接响应。若是改变Server状态的写请求,需要通过一致性协议来处理,这个协议就是我们前面介绍的Zab协议。
简单来说,Zab协议规定:**来自Client的所有写请求,都要转发给ZK服务中唯一的Server—Leader, 由Leader根据该请求发起一个Proposal。然后,其他的Server对该Proposal进行Vote。之后,Leader对Vote进行收 集,当Vote数量过半时Leader会向所有的Server发送一个通知消息。**最后,当Client所连接的Server收到该消息时,会把该操作更新 到内存中并对Client的写请求做出回应。该工作流程如下图1.2所示。
从图中我们可以看出, ZooKeeper 服务器在上述协议中实际扮演了两个职能。它们一方面从客户端接受连接与操作请求,另一方面对操作结果进行投票。这两个职能在 ZooKeeper集群扩展的时候彼此制约。例如,当我们希望增加 ZK服务中Client数量的时候,那么我们就需要增加Server的数量,来支持这么多的客户端。然而,从Zab协议对写请求的处理过程中我们可以发 现,增加服务器的数量,则增加了对协议中投票过程的压力。因为Leader节点必须等待集群中过半Server响应投票,于是节点的增加使得部分计算机运 行较慢,从而拖慢整个投票过程的可能性也随之提高,写操作也会随之下降。这正是我们在实际操作中看到的问题——随着 ZooKeeper 集群变大,写操作的吞吐量会下降。

Observer可以接受客户端的连接,并将写请求转发给Leader节点。但是,Leader节点不会要求 Observer参加投票。相反,Observer不参与投票过程,仅仅在上述第3歩那样,和其他服务节点一起得到投票结果。

ZooKeeper的一致性保证

CAP理论

(1) 理论概述
分布式领域中存在CAP理论:
① C:Consistency,一致性,数据一致更新,所有数据变动都是同步的。
② A:Availability,可用性,系统具有好的响应性能。
③ P:Partition tolerance,分区容错性。以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择,也就是说无论任何消息丢失,系统都可用。
该理论已被证明:任何分布式系统只可同时满足两点,无法三者兼顾。 因此,将精力浪费在思考如何设计能满足三者的完美系统上是愚钝的,应该根据应用场景进行适当取舍。

一致性分类

一致性是指从系统外部读取系统内部的数据时,在一定约束条件下相同,即数据变动在系统内部各节点应该是同步的。根据一致性的强弱程度不同,可以将一致性级别分为如下几种:
① 强一致性(strong consistency)。任何时刻,任何用户都能读取到最近一次成功更新的数据。
② 单调一致性(monotonic consistency)。任何时刻,任何用户一旦读到某个数据在某次更新后的值,那么就不会再读到比这个值更旧的值。也就是说,可获取的数据顺序必是单调递增的。
③ 会话一致性(session consistency)。任何用户在某次会话中,一旦读到某个数据在某次更新后的值,那么在本次会话中就不会再读到比这个值更旧的值。会话一致性是在单调一致性的基础上进一步放松约束,只保证单个用户单个会话内的单调性,在不同用户或同一用户不同会话间则没有保障。
④ 最终一致性(eventual consistency)。用户只能读到某次更新后的值,但系统保证数据将最终达到完全一致的状态,只是所需时间不能保障。
⑤ 弱一致性(weak consistency)。用户无法在确定时间内读到最新更新的值。

ZooKeeper与CAP理论

我们知道ZooKeeper也是一种分布式系统,它在一致性上有人认为它提供的是一种强一致性的服务(通过sync操作),也有人认为是单调一致性(更新时的大多说概念),还有人为是最终一致性(顺序一致性),反正各有各的道理这里就不在争辩了。然后它在分区容错性和可用性上做了一定折中,这和CAP理论是吻合的。

ZooKeeper从以下几点保证了数据的一致性

  1. 顺序一致性
    来自任意特定客户端的更新都会按其发送顺序被提交。也就是说,如果一个客户端将Znode z的值更新为a,在之后的操作中,它又将z的值更新为b,则没有客户端能够在看到z的值是b之后再看到值a(如果没有其他对z的更新)。
  2. 原子性
    每个更新要么成功,要么失败。这意味着如果一个更新失败,则不会有客户端会看到这个更新的结果。
  3. 单一系统映像
    一 个客户端无论连接到哪一台服务器,它看到的都是同样的系统视图。这意味着,如果一个客户端在同一个会话中连接到一台新的服务器,它所看到的系统状态不会比 在之前服务器上所看到的更老。当一台服务器出现故障,导致它的一个客户端需要尝试连接集合体中其他的服务器时,所有滞后于故障服务器的服务器都不会接受该 连接请求,除非这些服务器赶上故障服务器

ZK集群中每个Server,都保存一份数据副本。Zookeeper使用简单的同步策略,通过以下两条基本保证来实现数据的一致性:
① 全局串行化所有的写操作
② 保证同一客户端的指令被FIFO执行(以及消息通知的FIFO)
所有的读请求由Zk Server 本地响应,所有的更新请求将转发给Leader,由Leader实施。

4. 持久性
一个更新一旦成功,其结果就会持久存在并且不会被撤销。这表明更新不会受到服务器故障的影响。

zk实现分布式锁

Zookeeper实现分布式锁原理:
Zookeeper节点路径不能重复 保证唯一性。 临时节点+事件通知

获取锁方法:

多个jvm同时在zk上创建一个临时节点/lockPath,
最终只能够有一个jvm创建临时节点成功,如果能够创建
临时节点成功jvm 表示获取锁成功能够正常执行业务逻辑,
如果没有创建临时节点成功的jvm,则表示获取锁失败。
获取锁失败之后,可以采用不断重试策略,重试多次
获取锁失败之后,当前的jvm就进入到阻塞状态。

释放锁方法:

直接调用.close();释放锁
因为采用临时节点,当我们调用close()方法的时候
该临时节点会自动被删除。
其他没有获取到锁的jvm,就会从新进入到获取锁的状态。

被唤醒的方法:

被阻塞的jvm(没有获取锁成功的jvm),采用事件监听的方式
监听到节点已经被删除的情况下,则开始从新进入到获取锁的状态。

你可能感兴趣的:(分布式,#,分布式锁,zookeeper,分布式)