信誉好的大连网站建设,锐奇智能手机网站建设,网站开发网络,小广告推广网站分布式协调技术
在学习ZooKeeper之前需要先了解一种技术——分布式协调技术。那么什么是分布式协调技术#xff1f;其实分布式协调技术主要用来解决分布式环境当中多个进程之间的同步控制#xff0c;让他们有序的去访问某种临界资源#xff0c;防止造成脏数据的…分布式协调技术
在学习ZooKeeper之前需要先了解一种技术——分布式协调技术。那么什么是分布式协调技术其实分布式协调技术主要用来解决分布式环境当中多个进程之间的同步控制让他们有序的去访问某种临界资源防止造成脏数据的后果。这时有人可能会说这个简单写一个调度算法就轻松解决了。说这句话的人可能对分布式系统不是很了解所以才会出现这种误解。如果这些进程全部是跑在一台机上的话相对来说确实就好办了问 题就在于他是在一个分布式的环境下这时问题又来了那什么是分布式呢可以通过下面这张图帮助大家理解这方面的内容如下图所示。 给大家分析一下这张图在这图中有三台机器每台机器各跑一个应用程序。然后我们将这三台机器通过网络将其连接起来构成一个系统来为用户提供服务对用户来说这个系统的架构是透明的他感觉不到我这个系统是一个什么样的架构。那么我们就可以把这种系统称作一个分布式系统。 那我们接下来再分析一下在这个分布式系统中如何对进程进行调度我假设在第一台机器上挂载了一个资源然后这三个物理分布的进程都要竞争这个资源但我们又不希望他们同时进行访问这时候我们就需要一个协调器来让他们有序的来访问这个资源。这个协调器就是我们经常提到的那个锁比如说进程A在使用该资源的时候会先去获得锁进程A获得锁以后会对该资源保持独占这样其他进程就无法访问该资源进程A用完该资源以后就将锁释放掉让其他进程来获得锁那么通过这个锁机制我们就能保证了分布式系统中多个进程能够有序的访问该临界资源。那么我们把这个分布式环境下的这个锁叫作分布式锁。这个分布式锁也就是我们分布式协调技术实现的核心内容那么如何实现这个分布式呢那就是我们后面要讲的内容。
分布式锁的实现
面临的问题
在看了上图所示的分布式环境之后有人可能会感觉这不是很难。无非是将原来在同一台机器上对进程调度的原语通过网络实现在分布式环境中。是的表面上是可以这么说。但是问题就在网络这在分布式系统中所有在同一台机器上的假设都不存在因为网络是不可靠的。 比如在同一台机器上你对一个服务的调用如果成功那就是成功如果调用失败比如抛出异常那就是调用失败。但是在分布式环境中由于网络的不可靠你对一个服务的调用失败了并不表示一定是失败的可能是执行成功了但是响应返回的时候失败了。还有A和B都去调用C服务在时间上 A还先调用一些B后调用那么最后的结果是不是一定A的请求就先于B到达呢 这些在同一台机器上的种种假设我们都要重新思考我们还要思考这些问题给我们的设计和编码带来了哪些影响。还有在分布式环境中为了提升可靠性我们往往会部署多套服务但是如何在多套服务中达到一致性这在同一台机器上多个进程之间的同步相对来说比较容易办到但在分布式环境中确实一个大难题。 所以分布式协调远比在同一台机器上对多个进程的调度要难得多而且如果为每一个分布式应用都开发一个独立的协调程序。一方面协调程序的反复编写浪费且难以形成通用、伸缩性好的协调器。另一方面协调程序开销比较大会影响系统原有的性能。所以急需一种高可靠、高可用的通用协调机制来用以协调分布式应用。
分布式锁的实现者
目前在分布式协调技术方面做得比较好的就是Google的Chubby还有Apache的ZooKeeper他们都是分布式锁的实现者。有人会问既然有了Chubby为什么还要弄一个ZooKeeper难道Chubby做得不够好吗不是这样的主要是Chbby是非开源的Google自家用。后来雅虎模仿Chubby开发出了ZooKeeper也实现了类似的分布式锁的功能并且将ZooKeeper作为一种开源的程序捐献给了Apache那么这样就可以使用ZooKeeper所提供锁服务。而且在分布式领域久经考验它的可靠性可用性都是经过理论和实践的验证的。所以我们在构建一些分布式系统的时候就可以以这类系统为起点来构建我们的系统这将节省不少成本而且bug也 将更少。
Zookeeper介绍
ZooKeeper 是分布式应用程序的分布式开源协调服务。它公开了一组简单的原语分布式应用程序可以基于这些原语实现更高级别的同步、配置维护、组和命名服务等。它被设计为易于编程并使用一种数据模型该模型以熟悉的文件系统目录树结构为风格。它在 Java 中运行并具有 Java 和 C 的绑定。众所周知协调服务很难做好。它们特别容易出现竞争条件和死锁等错误。ZooKeeper背后的动机是减轻分布式应用程序从头开始实现协调服务的责任。
设计目标
ZooKeeper 很简单。ZooKeeper 允许分布式进程通过共享的分层命名空间相互协调该命名空间的组织方式类似于标准文件系统。命名空间由数据寄存器组成——在 ZooKeeper 用语中称为 znodes——它们类似于文件和目录。然后在该数据结构的基础上定义了一些原语也就是一些关于该数据结构的一些操作。有了这些数据结构和原语还不够因为我们的ZooKeeper是工作在一个分布式的环境下我们的服务是通过消息以网络的形式发送给我们的分布式应用程序所以还需要一个通知机制——Watcher机制。综上ZooKeeper所提供的服务主要是通过数据结构原语watcher机制三个部分来实现的。与为存储而设计的典型文件系统不同ZooKeeper 数据保存在内存中这意味着 ZooKeeper 可以实现高吞吐量和低延迟数字。
ZooKeeper 实施非常重视高性能、高可用性、严格有序的访问。ZooKeeper 的性能方面意味着它可以用于大型分布式系统。可靠性方面使其不会成为单点故障。严格的排序意味着可以在客户端实现复杂的同步原语。
ZooKeeper 被复制。与它协调的分布式进程一样ZooKeeper 本身旨在通过一组称为 ensemble 的主机进行复制。
组成 ZooKeeper 服务的服务器必须相互了解。它们在内存中维护状态图像以及持久存储中的事务日志和快照。只要大多数服务器可用ZooKeeper 服务就可用。
客户端连接到单个 ZooKeeper 服务器。客户端维护一个 TCP 连接通过它发送请求、获取响应、获取监视事件并发送心跳。如果与服务器的 TCP 连接中断客户端将连接到不同的服务器。
ZooKeeper 已订购。ZooKeeper 使用反映所有 ZooKeeper 事务顺序的数字标记每个更新。后续操作可以使用该顺序来实现更高级别的抽象例如同步原语。
ZooKeeper 速度很快。它在“以读取为主”的工作负载中尤其快。ZooKeeper 应用程序在数千台机器上运行它在读取比写入更常见的情况下表现最佳比率约为 10:1。
zookeeper数据模型和分层命名空间
ZooKeeper 提供的命名空间很像标准文件系统。名称是由斜杠 (/) 分隔的一系列路径元素。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只能被触发一次这样可以减少网络流量。 3.6.0 中的新功能客户端还可以在 znode 上设置永久的递归监视这些监视在触发时不会被删除并且会以递归方式触发已注册 znode 以及任何子 znode 上的更改。
ZooKeeper中的时间
致使ZooKeeper节点状态改变的每一个操作都将使节点接收到一个Zxid格式的时间戳并且这个时间戳全局有序。也就是说每个对节点的改变都将产生一个唯一的Zxid。如果Zxid1的值小于Zxid2的值那么Zxid1所对应的事件发生在Zxid2所对应的事件之前。实际上ZooKeeper的每个节点维护者三个Zxid值为别为cZxid、mZxid、pZxid。 ① cZxid 是节点的创建时间所对应的Zxid格式时间戳。 ② mZxid是节点的修改时间所对应的Zxid格式时间戳。 ③ pZxid 是与该节点的子节点或该节点的最近一次 创建/删除 的时间戳对应
实现中Zxid是一个64为的数字它高32位是epoch用来标识leader关系是否改变每次一个leader被选出来它都会有一个 新的epoch。低32位是个递增计数版本号 对节点的每一个操作都将致使这个节点的版本号增加。每个节点维护着三个版本号他们分别为 ① version节点数据版本号 ② cversion子节点版本号 ③ aversion节点所拥有的ACL版本号
ZooKeeper节点属性
通过前面的介绍我们可以了解到一个节点自身拥有表示其状态的许多重要属性如下图所示。 Znode节点属性结构
属性描述czxid节点被创建的zxidmzxid节点被修改的zxid .ctime节点被创建的时间mtime节点被修改的zxidversion节点被修改的版本号cversion节点所拥有的子节点被修改的版本号aversion节点的ACL被修改的版本号ephemeralowner如果此节点为临时节点那么他的值为这个节点拥有者的会话ID;否则他的值为0dataLength节点数长度numChildren节点用的子节点长度pzxid最新修改的zxid,貌似与mzxid重合了
ZooKeeper中的数据保证
ZooKeeper 非常快速且非常简单。但是由于它的目标是成为构建更复杂服务例如同步的基础因此它提供了一组保证。这些都是
顺序一致性 - 来自客户端的更新将按照它们发送的顺序应用。原子性 - 更新成功或失败。没有部分结果。单一系统映像 - 客户端将看到相同的服务视图而不管它连接到的服务器如何。即即使客户端故障转移到具有相同会话的不同服务器客户端也永远不会看到系统的旧视图。可靠性 - 应用更新后它将从那时起持续存在直到客户端覆盖更新。及时性——系统的客户视图保证在一定的时间范围内是最新的。
zookeeper简单的 API操作
ZooKeeper 的设计目标之一是提供一个非常简单的编程接口。因此它仅支持以下操作 create在树中的某个位置创建一个节点 delete : 删除一个节点 exists测试节点是否存在于某个位置 getACL获取数据从节点读取数据 setACL设置数据将数据写入节点 getChildren 获取子节点检索节点的子节点列表 sync等待数据传播
执行
ZooKeeper 组件展示了 ZooKeeper 服务的高级组件。除了请求处理器之外组成 ZooKeeper 服务的每个服务器都复制自己的每个组件的副本。
复制数据库是包含整个数据树的内存数据库。更新被记录到磁盘以便恢复写入在应用到内存数据库之前被序列化到磁盘。 每个 ZooKeeper 服务器都服务于客户端。客户端仅连接到一台服务器以提交请求。从每个服务器数据库的本地副本为读取请求提供服务。改变服务状态的请求写请求由协议协议处理。 作为协议协议的一部分来自客户端的所有写入请求都被转发到单个服务器称为领导者。ZooKeeper 服务器的其余部分称为追随者接收来自领导者的消息提议并同意消息传递。消息传递层负责在失败时替换领导者并将追随者与领导者同步。
ZooKeeper 使用自定义原子消息传递协议。由于消息传递层是原子的ZooKeeper 可以保证本地副本永远不会发散。当领导者收到一个写请求时它会计算系统在应用写时的状态并将其转换为捕获这个新状态的事务。
用途 ZooKeeper 的编程接口故意简单。但是使用它您可以实现更高阶的操作例如同步原语、组成员资格、所有权等。
ZooKeeper应用实例
为了方便大家理解ZooKeeper用以下例子说明ZooKeeper是如何实现的他的服务的以ZooKeeper提供的基本服务分布式锁为例。
分布式锁应用场景
在分布式锁服务中有一种最典型应用场景就是通过对集群进行Master选举来解决分布式系统中的单点故障。什么是分布式系统中的单点故障通常分布式系统采用主从模式就是一个主控机连接多个处理节点。主节点负责分发任务从节点负责处理任务当我们的主节点发生故障时那么整个系统就都瘫痪了那么我们把这种故障叫作单点故障。如下图所示 传统解决方案
传统方式是采用一个备用节点这个备用节点定期给当前主节点发送ping包主节点收到ping包以后向备用节点发送回复Ack当备用节点收到回复的时候就会认为当前主节点还活着让他继续提供服务。如下图所示 而当主节点挂了这时候备用节点收不到回复了然后他就认为主节点挂了接替他成为主节点。如下图所示 但是这种方式就是有一个隐患就是网络问题来看一网络问题会造成什么后果如下图7.5所示 也就是说我们的主节点的并没有挂只是在回复的时候网络发生故障这样我们的备用节点同样收不到回复就会认为主节点挂了然后备用节点将他的Master实例启动起来这样我们的分布式系统当中就有了两个主节点也就是双Master出现Master以后我们的从节点就会将它所做的事一部分汇报给了主节点一部分汇报给了从节点这样服务就全乱了。为了防止出现这种情况我们引入了ZooKeeper它虽然不能避免网络故障但它能够保证每时每刻只有一个Master。下面来看一下ZooKeeper是如何实现的。
ZooKeeper解决方案
Master启动
在引入了Zookeeper以后我们启动了两个主节点“主节点-A和主节点-B他们启动以后都向ZooKeeper去注册一个节点。我们假设主节点-A锁注册地节点是master-00001”“主节点-B注册的节点是master-00002”注册完以后进行选举编号最小的节点将在选举中获胜获得锁成为主节点也就是我们的主节点-A将会获得锁成为主节点然后主节点-B将被阻塞成为一个备用节点。那么通过这种方式就完成了对两个Master进程的调度。
Master故障
如果主节点-A挂了这时候他所注册的节点将被自动删除ZooKeeper会自动感知节点的变化然后再次发出选举这时候主节点-B将在选举中获胜替代主节点-A成为主节点。 Master 恢复 如果主节点恢复了他会再次向ZooKeeper注册一个节点这时候他注册的节点将会是master-00003ZooKeeper会感知节点的变化再次发动选举这时候主节点-B在选举中会再次获胜继续担任主节点主节点-A会担任备用节点。
更多关于zookeeper的知识分享请前往博客主页。编写过程中难免出现差错敬请指出