Zookeeper基本功能和应用场景

Zookeeper基本功能和应⽤场景
1. 简介
Zookeeper是⼀个开源的分布式的,为分布式应⽤提供协调服务的Apache项⽬。是⼤数据Hadoop⽣态体系中⽤的⾮常⼴泛的基础组件2. Zookeeper基本功能和应⽤场景
2.1 基本功能
⽂件系统:zookeeper可以为客户端管理状态信息,虽然存储的数量较⼩(每个节点默认不可超过1M)
(k - v):
/aa/bb "hello"
/aa    "word"
303c
/cc    "hadoop"
通知机制:可以为客户端监听指定数据节点的状态,并在数据节点发⽣改变时,通知客户端
总上所述,我们可以认为 zookeeper = ⽂件系统 + 通知机制
2.3 Zookeeper应⽤场景
2.3.1 数据发布和订阅
数据发布/订阅系统,需要发布者将数据发布到zk的节点上,供订阅者进⾏数据订阅,进⽽达到动态获取数据的⽬的,实现配置信息的集中式管理和数据的动态更新。
发布/订阅⼀般有两种设计模式:推模式和拉模式,服务端主动将数据更新发送给所有的订阅的客户端称为推模式;客户端注册请求获取最新数据称为拉模式,zk采⽤了推拉相结合的模式,客户端将服务器注册⾃⼰需要关注的节点,⼀旦节点数据发⽣变更,那么服务器就会向相应的客户端推送Watcher事件通知,客户端接收到此通知后,主动到服务端获取最新的数据。
注意:对于像Dubbo这样的RPC框架来说,zk将作为注册中⼼,客户端第⼀次通过向zk集获取服务的地址,然后会存储到本地,下⼀次进⾏调⽤时就不会再去zk集查询了,⽽是直接使⽤本地存储的地址,只要当服务地址发⽣变更时,才会通知客户端再次获取。
在平时开发中,经常会碰到这样的需求:系统中需要使⽤⼀些通⽤的配置信息,例如:机器列表信息,数据的配置信息(⽐如:要实现数据库切换的应⽤场景),运⾏时的开关配置等。这些全局配置信息通常有3个特性:数据量通常⽐较⼩;数据内容在运⾏时会发⽣动态变
化;集中各机器共享、配置⼀致。假设,我们的集规模很⼤,且配置信息经常变更,所以通过存储本地配置⽂件或内存变量的形式实现都很困难,这时就需要zk来做⼀个全局配置信息管理。
2.3.2 负载均衡
负载均衡是⼀种相当常见的计算机⽹络技术,⽤来对多个计算机、⽹络连接、CPU、磁盘驱动或其他资源进⾏分配负载,已达到优化资源使⽤、最⼤化吞吐量、最⼩化响应时间和避免过载的⽬的。
通常负载均衡可以分为硬件(F5)和软件(Nginx)负载均衡两类。Zookeeper也可以作为实现软负载的⼀种形式。
分布式系统为保证可⽤性,通常通过副本的⽅式对数据和服务进⾏部署,⽽对于客户端来说,只需要在这样对等的服务提供⽅中选择⼀个执⾏相关的业务逻辑,怎么选,这就是负载均衡的应⽤。
孔板波纹规整填料⽐如,典型的需要负载均衡的DNS(Domain Name System)服务,我们可以⽤zookeeper实现动态的DNS⽅案(具体可⾃⾏百度)
zk实现负载均衡就是通过 watcher 机制和临时节点判断哪些节点宕机来获取可⽤的节点来实现的:
卷帘门门板
zk会维护⼀个树形的数据结构,类似于window的资源管理器⽬录,其中 EPHEMERAL(临时)节点会随着创建它的客户端端⼝⽽被删除,利⽤这个特性很容易实现软负载均衡。
基本原理:每个应⽤的Server启动时创建⼀个 EPHEMERAL 节点,应⽤客户端通过读取节点列表获得可⽤服务器列表,并订阅节点事件,有Server宕机断开时触发事件,客户端监测到后把Server从可⽤列表中删除。
消息中间件中发布者和订阅者的负载均衡,linkedin开源的KafkaMQ和阿⾥开源的MetaQ都是通过Zookeeper来做⽣产者、消费者负载均衡的。
2.3.3 命名服务
命名服务是分布式系统中较为常见的⼀类场景,分布式系统中,被命名的实体通常可以是集中的机器、提供的服务地址或远程对象等。通过命名服务,客户端可以根据指定名字来获取资源的实体、服务地址和提供者信息,最为创建的就是RPC框架的服务地址列表命名。
zk也可帮助应⽤系统通过资源引⽤的⽅式来实现对象资源的定位和使⽤,⼴义上的命名服务的资源定位都不是真正意义的实体资源,在分布式环境中,上层应⽤仅仅需要⼀个全局唯⼀的名字。zk可以实现⼀套分布式全局唯⼀ID的分配机制。(⽤UUID的⽅式问题在于⽣成的字符串过长,浪费存储空间且字符串⽆规律不利于开发调试)通过调⽤zk节点创建API接⼝就可以创建⼀个顺序节点,并且在API返回值中返回这个节点的完整名称,利⽤此特性,可以⽣成全局ID,其步骤如下:
1. 客户端根据任务类型,在指定类型的任务下通过调⽤接⼝创建⼀个顺序节点,如"job-".
2. 创建完成后,会返回⼀个完整的节点名,如"job-0000001".
3. 客户端拼接type类型和返回值后,就可以作为全局唯⼀的ID 了,如"type2-job-0000001".
阿⾥开源分布式服务框架Dubbo中使⽤zookeeper来作为其命名服务,维护全局的服务列表。在Dubbo实现中:
服务提供者:启动的时候,向zk的指定节点 /dubbo/${serverName}/providers ⽬录下写⼊⾃⼰的URL地址,这个操作完成了服务的发布。
服务消费者:启动的时候,订阅 /dubbo/${serverName}/providers ⽬录下的提供者URL地址,并向 /dubbo/${serverName}/consumers ⽬录下写⼊⾃⼰的URL地址。
注意:所有向ZK上注册的地址都是临时节点,这样就能保证服务提供者和消费者能够⾃动感应资源的变化。另外,Dubbo还有针对服务粒度的监控,⽅法是订阅/dubbo/${serverName}⽬录下的所有提供者和消费者信息。
2.3.4 分布式协调通知
zk中特有的 Watcher注册于异步通知机制,能够很好地实现分布式环境下不同机器,甚⾄不同系统之间的协调和通知,从⽽实现对数据变更的实时处理。通常的做法是不同的客户端对zk上同⼀个数据节点进⾏Watcher注册,监听数据节点的变化(包括节点本⾝和⼦节点),若数据节点发⽣变化,那么所有订阅的客户端都能够接受到相应的Watcher通知,并作出相应的处理。
在绝⼤多数分布式系统中,系统机器间的通信⽆外乎⼼跳检测、⼯作进度汇报和系统调度。这三种类型的机器通信⽅式都可以使⽤zookeeper来实现:
1. ⼼跳检测:不同机器间需要检测到彼此知否在正常运⾏,可以使⽤zk实现机器间的⼼跳检测,基于其临时节点特性(临时节点的⽣存
周期是客户端会话,客户端若宕机后,其临时节点⾃然不再存在),可以让不同机器都在zookeeper的指定节点下创建临时⼦节点,不同的机器之间可以根据这个临时⾃⼰点来判断对应的客户端机器是否存活。通过zookeeper可以⼤⼤减少系统耦合。
2. ⼯作进度汇报:通常任务被分发到不同的机器后,需要实时的将⾃⼰的任务执⾏进度汇报给分发系统,可以在zookeeper上选择⼀个
节点,每个任务客户端都在这个节点下⾯创建临时⼦节点,这不仅可以判断机器是否存活,同时各个机器可以将⾃⼰的任务执⾏进度写到该临时⼦节点中去,以便中⼼系统能够实时获取任务的执⾏进度。
3. 系统调度:zookeeper能够实现如下系统调度模式:分布式系统有控制台和⼀些客户端系统两部分构成,控制台的职责就是需要将⼀
些指令信息发送给所有的客户端,以控制他们进⾏相应的业务逻辑,后台管理⼈员在控制台上做⼀些操作,实际上就是修改
zookeeper上某个节点的数据,zookeeper可以把数据变更以时间通知的形式发送给订阅客户端。
2.3.5 集管理
zookeeper的两⼤特性:节点特性和watcher机制
对在zookeeper上创建的临时节点,⼀旦客户端与服务器之间的会话失效,那么临时节点就会被⾃动删除。
客户端如果对zookeeper的数据节点注册watcher监听,那么当该数据节点内容或是其⼦节点列表发⽣变更时,zookeeper服务器就会向订阅的客户端发送变更通知。
机器在线率有较⾼要求的场景,能够快速对集中机器变化做出响应。这样的场景中,往往有⼀个监控系统,实时监测集机器是否存活。过去的做法通常是:监控系统通过某种⼿段(⽐如ping)定时监测每个机器,或者每个机器⾃⼰定时向监控系统汇报“我还活着”。这种做法可⾏,但是存在两个⽐较明显的问题:
1. 集机器有变动的时候,牵连修改的东西⽐较多。
2. 有⼀定的延时。
利⽤zookeeper的两个特性,就可以实现另⼀种集机器存活性监控系统。若监控系统在 /clusterServers 节点上注册⼀个 watcher监听,那么但凡进⾏动态添加机器的操作,就会在 /clusterServices 节点下创建⼀个临时节点:/clusterService/[Hostname],这样,监控系统就能够实时监测机器的变动情况。
下⾯通过分布式⽇志收集系统的典型应⽤来学习zookeeper如何实现集管理。
分布式⽇志收集系统的核⼼⼯作就是收集分布在不同机器上的系统⽇志,在典型的⽇志系统架构设计中,整个⽇志系统会把所有需要收集的⽇志机器分为多个组别,每个组别对应⼀个收集器,这个收集器其实就是⼀个后台机器,⽤于收集⽇志,对于⼤规模的分布式⽇志收集系统场景,通常需要解决两个问题:
变化的⽇志源机器
变化的收集器机器
⽆论是⽇志源机器还是收集器机器的变更,最终都可以归结为如何快速、合理、动态地为每个收集器分配对应的⽇志源机器。
1. 注册收集器机器,在zookeeper上创建⼀个节点作为收集器的根节点,例如 /logs/collector 的收集器节点,每个收集器机器启动时
都会在收集器节点上创建⾃⼰的⼦节点,如/logs/collector/[Hostname]曲度腰枕仪
2. 任务分发,所有的收集器机器都创建完对应的节点后,系统根据收集器节点下⼦节点的个数,将所有的⽇志源机器分成对应的若⼲
组,然后将分组后的机器列表分别写到这些收集器创建的⼦节点,如 /logs/collector/host1(持久节点)上去。这样,收集器机器就能够根据⾃⼰对应的收集器节点上获取⽇志源机器列表,进⽽开始进⾏⽇
志的收集⼯作。
3. 状态汇报,完成分发后,机器随时会宕机,所以需要⼀个收集器的状态汇报机制,每个收集器机器上创建完节点后,还需要在对应的
⼦节点上创建⼀个状态⼦节点,如/logs/collector/host/status(临时节点),每个收集器机器都需要定期向该节点写⼊⾃⼰的状态信息,这可看做是⼼跳检测机制,通常收集器机器都会写⼊⽇志收集状态信息,⽇志系统通过判断状态⼦节点最后的变更时间,⼀旦有机器停⽌汇报或有新机器加⼊,就开始进⾏任务的重新分配,此时通常有两种做法:
1. 全局动态分配,当收集器宕机或有新的机器加⼊,系统根据新的收集器列表,⽴即对所有的⽇志源机器重新进⾏⼀次分组,然后
将其分配给剩下的收集器机器。
2. 局部动态分配,每个收集器机器在汇报⾃⼰⽇志收集状态的同时,也会把⾃⼰的负载汇报上去,如果⼀个机器宕机了,那么⽇志
系统会把之前分配给这个机器的任务重新分配给那些负载较低的机器,同样,如果有新的机器加⼊,会从那些负载较⾼的机器上转移⼀部分任务给新机器。
2.3.6 Master选举
在分布式系统中,Master往往⽤来协调集中其他系统单元,具有对分布式系统状态变更的决定权,如在读写分离的应⽤场景中,客户端的写请求往往是有Master来处理,或者其常常处理⼀些复杂的逻辑并将其处理结果同步给其他系统单元。利⽤zookeeper的⼀致性,能够很好的保证在分布式⾼并发情况下节点的创建⼀定能够保证全局唯⼀性,即zookeeper将会保证客户端⽆法重复创建⼀个已经存在的数据节点(由其保证分布式数据的⼀致性)。
⾸先,创建/master_election/2019-10-09节点,客户端集每天会定时往该节点写创建临时节点,如/master_election/2019-10-
09/binding,这个过程中,只有⼀个客户端能够创建成功,此时其变成master,其他节点都会在节点/master_election/2019-10-09上注册⼀个节点变更的Watcher,⽤于监控当前的Master机器是否存活,⼀旦发现当前Master挂了,其余客户端将会重新进⾏Master选举。
另外,这种场景演化⼀下,就是动态Master选举。这就要⽤到 EPHEMERAL_SEQUENTIAL 类型节点的特性了。
上⾯提到,所有客户端创建请求,最终只有⼀个创建成功。在这⾥稍微变化下,就是允许所有请求都
创建成功,但是的有个创建顺序,于是所有的请求最终在zk上创建结果的⼀种情况是这样的:/currentMaster/{sessionId}-1,/currentMaster/{sessionId}-
2,/currentMaster/{sessionId}-3.....,每次选取序列号最⼩的那个机器作为Master,如果这个机器挂了,由于他创建的节点会马上消失,那么之后的最⼩那个机器就是Master了。
其在实际中应⽤有:
在搜索系统中,如果集中每个机器都⽣产⼀份全量索引,不仅耗时,⽽且不能保证彼此之间索引数据的⼀致。因此让集中的Master来进⾏全局索引的⽣成,然后通过到集中的其他机器。
在Hbase中,也是使⽤zookeeper来实现动态HMaster选举。在Hbase实现中,会在zk上存储⼀些ROOT表的地址和HMaster的地址,HRegionServer也会把即的以临时节点(Ephemeral)的⽅式注册在zk中,使得HMaster可以随时感知到各个HRegionServer 的存活状态,同时,⼀旦HMaster出现问题,会重新选举出⼀个HMaster来运⾏,从⽽避免了HMaster的单点问题。
2.3.7 分布式锁
分布式锁⽤于控制分布式系统之间同步访问共享资源的⼀种⽅式,可以保证不同系统访问⼀个或⼀组资源时的⼀致性,主要分为排它锁和共享锁。
排它锁⼜称写锁或独占锁,若事务T1对数据对象01加上了排它锁,那么整个加锁期间,只允许事务T1对01进⾏读取或更新操作,其他事务都不能再对整个数据对象进⾏任何类型的操作,直到T1释放了排它锁。
1. 获取锁,在需要获取排它锁时,所有客户端通过调⽤接⼝,在 /exclusive_lock 节点下创建临时⼦节点
/exclusive_lock/lock。zookeeper可以保证只有⼀个客户端能够创建成功,没有成功的客户端需要注册
/exclusive_lock节点监听。
2. 释放锁,当获取锁的客户端宕机或者正常完成业务逻辑都会导致临时节点删除,此时,所有在/exclusive_lock节点上注
册监听的客户端都会收到通知,可以重新发起分布式锁获取。
共享锁⼜称读锁,若事务T1对数据对象O1加了共享锁,那么当前事务是能对01进⾏读操作,其他事务只能对整个数据对象加共享锁,知道该数据对象上所有共享锁都被释放。
野营房
获取锁,当需要获取共享锁是,所有客户端都会到 /shared_lock 下⾯创建⼀个临时顺序节点,如果是读请求,那么就创建
如:/shared_lock/host-1-R0000001的节点,如果是写请求就创建例如:/shared_lock/host-2-W-0000002的节点,以此类推。
判断读写顺序,不同事务可以同时对⼀个数据对象进⾏读操作,⽽更新操作必须在当前没有任何事务进⾏读写情况下进⾏,通过zookeeper来确定分布式读写顺序,⼤致分为四步:
1. 创建完节点后,获取/shared_lock 节点下所有⼦节点,并对该节点变更注册监听。
2. 确定⾃⼰的节点序号在所有⼦节点中的顺序。
3. 对于读请求:若没有⽐⾃⼰序号⼩或所有⽐⾃⼰序号⼩的请求都是读请求,那么表明⾃⼰已经成功获取到共享锁,同时
开始执⾏读取逻辑,若有写请求,则需要等待。对于写请求:若⾃⼰不是序号最⼩的⼦节点,那么需要等待。
4. 接受到Watcher通知后,重复步骤1.
释放锁,其释放锁的流程和独占锁⼀致。
上述共享锁的实现⽅案,可以满⾜⼀般分布式集竞争锁的需求,但是如果机器规模扩⼤会出现⼀些问题,下⾯着重分析判断读写顺序的步骤3:
针对如上图所⽰的情况进⾏分析:
1. host1⾸先进⾏读操作,完成后将节点/shared_lock/host1-R-0000001删除。
2. 余下⼏台机器收到这个节点移除的通知,然后重新从/shared_lock节点上获取⼀份新的节点列表。
3. 每台机器判断⾃⼰的读写顺序,其中host2检测到⾃⼰的序号最⼩,于是进⾏写操作,余下机器则继续等待。
4. 继续......
可以看到,host1客户端在移除⾃⼰的共享锁后,zookeeper发送⼦节点变更Watcher通知给所有的机器,然后除了给host2产⽣影响外,对其他机器并没有任何作⽤。⼤量的Watcher通知和⼦节点列表获
取连个操作会重复执⾏,这样会造成系统性能影响和⽹络开销,更为严重的是,如果同⼀时间点有多个节点对应的客户端完成了事务或事务终端引起节点的消失,zookeeper服务器就会在短时间内向其他所有的客户端发送⼤量的事件通知,这就是所谓的⽺效应。
可以有如下改动来避免⽺效应:
1. 客户端调⽤create接⼝创建类似于/shared_lock/[Hostname]- 请求类型-序号的临时顺序节点。
2. 客户端调⽤getChildren接⼝获取所有已经创建的⼦节点列表(不注册任何watcher)。
3. 如果⽆法获取共享锁,就调⽤exist接⼝来对⽐⾃⼰⼩的节点注册Watcher。对于读请求:向⽐⾃⼰序号⼩的最后⼀个写请求节点注册Watcher监听。对于写请求:向⽐⾃⼰序号⼩的最后⼀个节点注册watcher监听。
4. 等待watcher通知,继续进⼊步骤2.
此⽅案改动主要在于:每个锁竞争者,只需要关注/shared_lock节点下序号⽐⾃⼰⼩的那个节点是否存在即可。
2.3.8 分布式队列
分布式队列可以简单分为 先⼊先出队列模式和等待队列元素聚集后统⼀安排处理执⾏的Barrier模型。
柴油车尾气处理**FIFO先⼊先出,先进⼊队列的请求操作先完成后,才会开始后⾯的请求。FIFO队列类似于全写的共享模式,所有客户端都会到/queue_fifo这个节点下创建临时节点,如/queue_fifo/host1-0000001。

本文发布于:2024-09-22 21:11:35,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/4/129100.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:节点   机器   客户端   系统   数据   创建   收集器
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议