Zookeeper 系列(一)基本概念

https://www.cnblogs.com/wuxl360/p/5817471.html

一、分布式协调技术

在给大家介绍 ZooKeeper 之前先来给大家介绍一种技术——分布式协调技术。那么什么是分布式协调技术?那么我来告诉大家,其实 分布式协调技术主要用来解决分布式环境当中多个进程之间的同步控制,让他们有序的去访问某种临界资源,防止造成"脏数据"的后果。 这时,有人可能会说这个简单,写一个调度算法就轻松解决了。说这句话的人,可能对分布式系统不是很了解,所以才会出现这种误解。如果这些进程全部是跑在一台机上的话,相对来说确实就好办了,问 题就在于他是在一个分布式的环境下,这时问题又来了,那什么是分布式呢?这个一两句话我也说不清楚,但我给大家画了一张图希望能帮助大家理解这方面的内 容,如果觉得不对尽可拍砖,来咱们看一下这张图,如图 1.1 所示。

给大家分析一下这张图,在这图中有三台机器,每台机器各跑一个应用程序。然后我们将这三台机器通过网络将其连接起来,构成一个系统来为用户提供服务,对用户来说这个系统的架构是透明的,他感觉不到我这个系统是一个什么样的架构。那么我们就可以把这种系统称作一个 分布式系统

那我们接下来再分析一下,在这个分布式系统中如何对进程进行调度,我假设在第一台机器上挂载了一个资源,然后这三个物理分布的进程都要竞争这个资源,但我们又不希望他们同时进行访问,这时候我们就需要一个 协调器 ,来让他们有序的来访问这个资源。这个协调器就是我们经常提到的那个锁,比如说"进程-1"在使用该资源的时候,会先去获得锁,"进程1"获得锁以后会对该资源保持 独占 ,这样其他进程就无法访问该资源,"进程1"用完该资源以后就将锁释放掉,让其他进程来获得锁,那么通过这个锁机制,我们就能保证了分布式系统中多个进程能够有序的访问该临界资源。那么我们把这个分布式环境下的这个锁叫作 分布式锁 。这个分布式锁也就是我们 分布式协调技术 实现的核心内容,那么如何实现这个分布式呢,那就是我们后面要讲的内容。

二、分布式锁的实现

好我们知道,为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁。那么这个锁怎么实现呢?这实现起来确实相对来说比较困难的。

2.1 面临的问题

在看了图 1.1 所示的分布式环境之后,有人可能会感觉这不是很难。无非是将原来在同一台机器上对进程调度的原语,通过网络实现在分布式环境中。是的,表面上是可以这么说。但是问题就在网络这,在分布式系统中,所有在同一台机器上的假设都不存在:因为网络是不可靠的。

比如,在同一台机器上,你对一个服务的调用如果成功,那就是成功,如果调用失败,比如抛出异常那就是调用失败。但是在分布式环境中,由于网络的不可 靠,你对一个服务的调用失败了并不表示一定是失败的,可能是执行成功了,但是响应返回的时候失败了。还有,A 和 B 都去调用 C 服务,在时间上 A 先调 B 后调用,那么最后的结果是不是一定 A 的请求就先于 B 到达呢? 这些在同一台机器上的种种假设,我们都要重新思考,我们还要思考这些问题给我们的设计和编码带来了哪些影响。还有,在分布式环境中为了提升可靠性,我们往往会部署多套服务,但是如何在多套服务中达到一致性,这在同一台机器上多个进程之间的同步相对来说比较容易办到,但在分布式环境中确实一个大难题。

所以分布式协调远比在同一台机器上对多个进程的调度要难得多,而且如果为每一个分布式应用都开发一个独立的协调程序。一方面,协调程序的反复编写浪费,且难以形成通用、伸缩性好的协调器。另一方面,协调程序开销比较大,会影响系统原有的性能。所以,急需一种高可靠、高可用的通用协调机制来用以协调分布式应用。

2.2 分布式锁的实现者

目前,在分布式协调技术方面做得比较好的就是 Google Chubby 还有 Apache ZooKeeper 他们都是分布式锁的实现者。有人会问 既然有了 Chubby 为什么还要弄一个 ZooKeeper,难道 Chubby 做得不够好吗?不是这样的,主要是 Chbby 是非开源的,Google 自家用。后来雅虎模仿 Chubby 开发出了 ZooKeeper,也实现了类似的分布式锁的功能,并且将 ZooKeeper 作为一种开源的程序捐献给了 Apache,那么这样就可以使用 ZooKeeper 所提供锁服务。而且在分布式领域久经考验,它的可靠性,可用性都是经过理论和实践的验证的。所以我们在构建一些分布式系统的时候,就可以以这类系统为起点来构建我们的系统,这将节省不少成本,而且 bug 也将更少。


三、ZooKeeper 概述

ZooKeeper 是一种为分布式应用所设计的高可用、高性能且一致的开源协调服务,它提供了一项基本服务:分布式锁服务。由于 ZooKeeper 的开源特性,后来我们的开发者在分布式锁的基础上,摸索了出了其他的使用方法:配置维护、组服务、分布式消息队列、分布式通知/协调等。

注意:ZooKeeper 性能上的特点决定了它能够用在大型的、分布式的系统当中。从可靠性方面来说,它并不会因为一个节点的错误而崩溃。除此之外,它严格的序列访问控制意味着复杂的控制原语可以应用在客户端上。ZooKeeper 在一致性、可用性、容错性的保证,也是 ZooKeeper 的成功之处,它获得的一切成功都与它采用的协议—— Zab 协议是密不可分的,这些内容将会在后面介绍。

前面提到了那么多的服务,比如分布式锁、配置维护、组服务等,那它们是如何实现的呢,我相信这才是大家关心的东西。ZooKeeper 在实现这些服务时,首先它设计一种新的数据结构—— Znode ,然后在该数据结构的基础上定义了一些 原语 ,也就是一些关于该数据结构的一些操作。有了这些数据结构和原语还不够,因为我们的 ZooKeeper 是工作在一个分布式的环境下,我们的服务是通过消息以网络的形式发送给我们的分布式应用程序,所以还需要一个通知机制—— Watcher 机制 。那么总结一下,ZooKeeper 所提供的服务主要是通过: 数据结构 + 原语 + watcher 机制 ,三个部分来实现的。那么我就从这三个方面,给大家介绍一下 ZooKeeper。

四、ZooKeeper 数据模型

4.1 ZooKeeper 数据模型 Znode

ZooKeeper 拥有一个层次的命名空间,这个和标准的文件系统非常相似,如下图 3.1 所示。

图4.1 ZooKeeper数据模型与文件系统目录树


从图中我们可以看出 ZooKeeper 的数据模型,在结构上和标准文件系统的非常相似,都是采用这种树形层次结构,ZooKeeper 树中的每个节点被称为 — Znode。和文件系统的目录树一样,ZooKeeper 树中的每个节点可以拥有子节点。但也有不同之处:

(1) 引用方式

Zonde 通过路径引用,如同 Unix 中的文件路径。路径必须是绝对的,因此他们必须由斜杠字符来开头。除此以外,他们必须是唯一的,也就是说每一个路径只有一个表示,因此这些路径不能改变。在 ZooKeeper 中,路径由 Unicode 字符串组成,并且有一些限制。字符串 "/zookeeper" 用以保存管理信息,比如关键配额信息。

(2) Znode 结构

ZooKeeper 命名空间中的 Znode,兼具文件和目录两种特点。既像文件一样维护着数据、元信息、ACL、时间戳等数据结构,又像目录一样可以作为路径标识的一部分。图中的每个节点称为一个 Znode。 每个 Znode 由 3 部分组成:

  • stat :此为状态信息, 描述该 Znode 的版本, 权限等信息
  • data :与该 Znode 关联的数据
  • children :该 Znode 下的子节点

ZooKeeper 虽然可以关联一些数据,但并没有被设计为常规的数据库或者大数据存储,相反的是,它用来管理调度数据,比如分布式应用中的配置文件信息、状态信息、汇集位置等等。这些数据的共同特性就是它们都是很小的数据,通常以 KB 为大小单位。ZooKeeper 的服务器和客户端都被设计为严格检查并限制每个 Znode 的数据大小至多 1M,但常规使用中应该远小于此值。

(3) 数据访问

ZooKeeper 中的每个节点存储的数据要被 原子性的操作 。也就是说读操作将获取与节点相关的所有数据,写操作也将替换掉节点的所有数据。另外,每一个节点都拥有自己的 ACL(访问控制列表),这个列表规定了用户的权限,即限定了特定用户对目标节点可以执行的操作。

(4) 节点类型

ZooKeeper 中的节点有两种,分别为临时节点和永久节点。节点的类型在创建时即被确定,并且不能改变。

  • 临时节点 :该节点的生命周期依赖于创建它们的会话。一旦会话(Session)结束,临时节点将被自动删除,当然可以也可以手动删除。虽然每个临时的 Znode 都会绑定到一个客户端会话,但他们对所有的客户端还是可见的。另外,ZooKeeper 的临时节点不允许拥有子节点。
  • 永久节点 :该节点的生命周期不依赖于会话,并且只有在客户端显示执行删除操作的时候,他们才能被删除。

(5) 顺序节点

当创建 Znode 的时候,用户可以请求在 ZooKeeper 的路径结尾添加一个递增的计数。这个计数对于此节点的父节点来说是唯一的,它的格式为"%10d"(10 位数字,没有数值的数位用 0 补充,例如"0000000001")。当计数值大于 232-1 时,计数器将溢出。

(6) 观察

客户端可以在节点上设置 watch,我们称之为监视器。当节点状态发生改变时(Znode 的增、删、改)将会触发 watch 所对应的操作。当watch 被触发时,ZooKeeper 将会向客户端发送且仅发送一条通知,因为 watch 只能被触发一次,这样可以减少网络流量。

4.2 ZooKeeper 中的时间

ZooKeeper 有多种记录时间的形式,其中包含以下几个主要属性:

(1) Zxid

致使 ZooKeeper 节点状态改变的每一个操作都将使节点接收到一个 Zxid 格式的时间戳,并且这个时间戳全局有序。也就是说,也就是说,每个对节点的改变都将产生一个唯一的 Zxid。如果 Zxid1 的值小于 Zxid2 的值,那么 Zxid1 所对应的事件发生在 Zxid2 所对应的事件之前。实际上,ZooKeeper 的每个节点维护者三个 Zxid 值,为别为:cZxid、mZxid、pZxid。

  1. cZxid : 是节点的创建时间所对应的 Zxid 格式时间戳。
  2. mZxid :是节点的修改时间所对应的 Zxid 格式时间戳。

实现中 Zxid 是一个 64 位的数字,它高 32 位是 epoch 用来标识 leader 关系是否改变,每次一个 leader 被选出来,它都会有一个 新的 epoch。低 32 位是个递增计数。

(2) 版本号

对节点的每一个操作都将致使这个节点的版本号增加。每个节点维护着三个版本号,他们分别为:

  1. version :节点数据版本号
  2. cversion :子节点版本号
  3. aversion :节点所拥有的ACL版本号

4.3 ZooKeeper 节点属性

通过前面的介绍,我们可以了解到,一个节点自身拥有表示其状态的许多重要属性,如下图所示。

五、ZooKeeper 服务中操作

在 ZooKeeper 中有 9 个基本操作,如下图所示:

更新 ZooKeeper 操作是有限制的。delete 或 setData 必须明确要更新的 Znode 的版本号,我们可以调用 exists 找到。如果版本号不匹配,更新将会失败。

更新 ZooKeeper 操作是非阻塞式的。因此客户端如果失去了一个更新(由于另一个进程在同时更新这个 Znode),他可以在不阻塞其他进程执行的情况下,选择重新尝试或进行其他操作。

尽管 ZooKeeper 可以被看做是一个文件系统,但是处于便利,摒弃了一些文件系统地操作原语。因为文件非常的小并且使整体读写的,所以不需要打开、关闭或是寻地的操作。

六、Watch 触发器

(1) watch 概述

ZooKeeper 可以为所有的读操作设置 watch,这些读操作包括:exists()、getChildren() 及 getData()。watch 事件是一次性的触发器,当 watch 的对象状态发生改变时,将会触发此对象上 watch 所对应的事件。watch 事件将被异步地发送给客户端,并且 ZooKeeper 为 watch 机制提供了有序的一致性保证。理论上,客户端接收 watch 事件的时间要快于其看到 watch 对象状态变化的时间。

(2) 事件类型

  1. EventType.NodeCreated:节点创建
  2. EventType.NodeDataChanged:节点数据变更
  3. EventType.NodeDeleted:节点删除
  4. EventType.NodeChildrenChanged:子节点创建、更新、删除

(3) watch 注册与处触发

exists()、getChildren() 及 getData() 均可以注册事件,如下:

  1. exists 操作上的 watch,在被监视的 Znode 创建、删除或数据更新时被触发。

  2. getData 操作上的 watch,在被监视的 Znode 删除或数据更新时被触发。在被创建时不能被触发,因为只有 Znode 一定存在,getData 操作才会成功。

  3. getChildren 操作上的 watch,在被监视的 Znode 的子节点创建或删除,或是这个 Znode 自身被删除时被触发。可以通过查看 watch 事件类型来区分是 Znode,还是他的子节点被删除:NodeDelete 表示 Znode 被删除,NodeDeletedChanged 表示子节点被删除。

Watch 由客户端所连接的 ZooKeeper 服务器在本地维护,因此 watch 可以非常容易地设置、管理和分派。当客户端连接到一个新的服务器 时,任何的会话事件都将可能触发 watch。另外,当从服务器断开连接的时候,watch 将不会被接收。但是,当一个客户端重新建立连接的时候,任何先前注册过的 watch 都会被重新注册。

(4) 需要注意的几点

Zookeeper 的 watch 实际上要处理两类事件:

  1. 连接状态事件(type=None, path=null)

    这类事件不需要注册,也不需要我们连续触发,我们只要处理就行了。

  2. 节点事件

    节点的建立,删除,数据的修改。它是 one time trigger,我们需要不停的注册触发,还可能发生事件丢失的情况。

上面 2 类事件都在 Watch 中处理,也就是重载的 process(Event event)

节点事件的触发,通过函数 exists,getData 或 getChildren 来处理这类函数,有双重作用:

  1. 注册触发事件
  2. 函数本身的功能

函数的本身的功能又可以用异步的回调函数来实现,重载 processResult() 过程中处理函数本身的的功能。

七、ZooKeeper 应用举例 

为了方便大家理解 ZooKeeper,在此就给大家举个例子,看看 ZooKeeper 是如何实现的他的服务的,我以 ZooKeeper 提供的基本服务分布式锁为例。

7.1 分布式锁应用场景

在分布式锁服务中,有一种最典型应用场景,就是通过对集群进行 Master 选举,来解决分布式系统中的单点故障。什么是分布式系统中的单点故障:通常分布式系统采用主从模式,就是一个主控机连接多个处理节点。主节点负责分发任务,从节点负责处理任务,当我们的主节点发生故障时,那么整个系统就都瘫痪了,那么我们把这种故障叫作单点故障。如下图 7.1 和 7.2 所示:

    

7.2 传统解决方案

传统方式是采用一个备用节点,这个备用节点定期给当前主节点发送 ping 包,主节点收到 ping 包以后向备用节点发送回复 Ack,当备用节点收到回复的时候就会认为当前主节点还活着,让他继续提供服务。如图 7.3 所示:

当主节点挂了,这时候备用节点收不到回复了,然后他就认为主节点挂了接替他成为主节点如下图 7.4 所示:

但是这种方式就是有一个隐患,就是网络问题,来看一网络问题会造成什么后果,如下图 7.5 所示:

也就是说我们的主节点的并没有挂,只是在回复的时候网络发生故障,这样我们的备用节点同样收不到回复,就会认为主节点挂了,然后备用节点将他的 Master 实例启动起来,这样我们的分布式系统当中就有了两个主节点也就是--- 双 Master , 出现 Master 以后我们的从节点就会将它所做的事一部分汇报给了主节点,一部分汇报给了从节点,这样服务就全乱了。为了防止出现这种情况,我们引入了 ZooKeeper,它虽然不能避免网络故障,但它能够保证每时每刻只有一个 Master。我么来看一下 ZooKeeper 是如何实现的。

7.3 ZooKeeper 解决方案

(1) Master启动

在引入了Zookeeper以后我们启动了两个主节点,"主节点-A"和"主节点-B"他们启动以后,都向ZooKeeper去注册一个节点。我们 假设"主节点-A"锁注册地节点是"master-00001","主节点-B"注册的节点是"master-00002",注册完以后进行选举,编号最 小的节点将在选举中获胜获得锁成为主节点,也就是我们的"主节点-A"将会获得锁成为主节点,然后"主节点-B"将被阻塞成为一个备用节点。那么,通过这 种方式就完成了对两个Master进程的调度。

(2) Master故障

如果"主节点-A"挂了,这时候他所注册的节点将被自动删除,ZooKeeper会自动感知节点的变化,然后再次发出选举,这时候"主节点-B"将在选举中获胜,替代"主节点-A"成为主节点。

(3) Master 恢复

如果主节点恢复了,他会再次向 ZooKeeper 注册一个节点,这时候他注册的节点将会是 "master-00003",ZooKeeper 会感知节点的变化再次发动选举,这时候"主节点-B"在选举中会再次获胜继续担任"主节点","主节点-A"会担任备用节点。


每天用心记录一点点。内容也许不重要,但习惯很重要!

Zookeeper 系列(一)基本概念的更多相关文章

  1. Zookeeper 系列(三)Zookeeper API

    Zookeeper 系列(三)Zookeeper API 本节首先介绍 Zookeeper 的 Shell 命令,再对 Java 操作 Zookeeper 的三种方式进行讲解,本节先介绍 Zookee ...

  2. 【ZooKeeper系列】2.用Java实现ZooKeeper API的调用

    温馨提示:在这里我再次提个小要求,希望大家能习惯看官方文档,文档虽然是英文但用词都比较简单,基本都能看懂文档表达的意思.授之以鱼不如授之以渔的道理相信大家都明白,也希望通过猿人谷的这个ZooKeepe ...

  3. [转帖]【ZOOKEEPER系列】Paxos、Raft、ZAB

    [ZOOKEEPER系列]Paxos.Raft.ZAB 2018-07-11 12:09:49 wangzy-nice 阅读数 2428更多 分类专栏: zookeeper   版权声明:本文为博主原 ...

  4. 【ZooKeeper系列】3.ZooKeeper源码环境搭建

    前文阅读: [ZooKeeper系列]1.ZooKeeper单机版.伪集群和集群环境搭建 [ZooKeeper系列]2.用Java实现ZooKeeper API的调用 在系列的前两篇文章中,介绍了Zo ...

  5. RxJava系列2(基本概念及使用介绍)

    RxJava系列1(简介) RxJava系列2(基本概念及使用介绍) RxJava系列3(转换操作符) RxJava系列4(过滤操作符) RxJava系列5(组合操作符) RxJava系列6(从微观角 ...

  6. ZooKeeper系列(1):安装搭建ZooKeeper环境

    ZooKeeper系列文章:https://www.cnblogs.com/f-ck-need-u/p/7576137.html#zk ZooKeeper有三种安装模式:单机安装(standalone ...

  7. ZooKeeper系列(2):ZooKeeper命令行工具zkCli.sh

    ZooKeeper系列文章:https://www.cnblogs.com/f-ck-need-u/p/7576137.html#zk 1.简介 ZooKeeper提供了一个非常简单的命令行客户端zk ...

  8. ZooKeeper系列(3):znode说明和znode状态

    ZooKeeper系列文章:https://www.cnblogs.com/f-ck-need-u/p/7576137.html#zk 1.znode znode的官方说明:http://zookee ...

  9. ZooKeeper系列(4):ZooKeeper的配置文件详解

    ZooKeeper系列文章:https://www.cnblogs.com/f-ck-need-u/p/7576137.html#zk zkServer.sh读取的默认配置文件是$ZOOKEEPER_ ...

随机推荐

  1. OpenACC 梯度下降法求解线性方程的优化

    ▶ 书上第二章,用一系列步骤优化梯度下降法解线性方程组.才发现 PGI community 编译器不支持 Windows 下的 C++ 编译(有 pgCC 命令但是不支持 .cpp 文件,要专业版才支 ...

  2. DistCp 集群之间数据拷贝工具

    DistCp(分布式拷贝)是用于大规模集群内部和集群之间拷贝的工具.可以将数据拷贝到另个一集群,也可以将另一个集群的数据拷贝到本集群.

  3. three添加和移除对象

    创建场景在第一章的地方就讲过怎么样创建一个最基本的场景,这里不重复了html:部分 <!doctype html><html lang="en"><h ...

  4. leetcode13

    public class Solution { private int ChangeToInt(char c) { ; string s = c.ToString(); switch (s) { ca ...

  5. 表析LESS、Sass和Stylus的异同

    . 首页 博客园 联系我 前言:CSS预处理语言. 基本差别. 基本语法. 变量与作用域. 混合(Mixins). 嵌套实现后代选择器. 继承. 条件语句. 循环语句. 综合对比. 留言评论 返回顶部 ...

  6. JSON.Stringify()和JSON.parse()的比较使用

    1.  JSON.Stringify() 将一个对象解析成字符串 <script> function myonclick() { var value = $('select option: ...

  7. FMX TListView 搜索 Search

    FMX,FireMonkey,平台框架下TListView控件搜索.过滤. 查找功能确实强大!! 设置SearchVisibe属性为true,就自动显示搜索框,输入文字,就开始检索了,不用写一行代码! ...

  8. File 操作

    文件乱码 服务器地址 try-with-resource 属性文件获取 文件排序 文件过滤 文件下载 流文件传递 文件乱码: WINDOWS系统桌面默认使用GBK,Linux系统默认使用UTF-8. ...

  9. DB分布式 跨库分页

    DB分布式-两种方式 1. JDBC扩展     sharding-jdbc: 直接封装JDBC,代码迁移成本低,适用于任何连接池及ORM框架,JAR包提供服务,未使用中间层,不用额外部署,DBA无需 ...

  10. DateUtil日期处理

    package com.zjx.util; import java.text.SimpleDateFormat; import java.util.Date; public class DateUti ...