#0 系列目录#node
Zookeeper系列算法
【Zookeeper系列六】Zookeeper 工做原理session
Zookeeper源码数据结构
Zookeeper应用
ZooKeeper是一个高可用的分布式数据管理与系统协调框架
。基于对Paxos算法的实现,使该框架保证了分布式环境中数据的强一致性
,也正是基于这样的特性,使得ZooKeeper解决不少分布式问题。网上对ZK的应用场景也有很多介绍,本文将介绍比较经常使用的项目例子,系统地对ZK的应用场景进行一个分门归类的介绍。
值得注意的是,ZK并不是天生就是为这些应用场景设计的,都是后来众多开发者根据其框架的特性
,利用其提供的一系列API接口(或者称为原语集),摸索出来的典型使用方法
。所以,也很是欢迎读者分享你在ZK使用上的奇技淫巧。
#1 Zookeeper数据模型# Zookeeper 会维护一个具备层次关系的数据结构
,它很是相似于一个标准的文件系统,如图所示:
图中的每一个节点称为一个znode. 每一个znode由3部分组成:
znode的版本, 权限等信息
;关联的数据
;子节点
;Zookeeper 这种数据结构有以下这些特色:
每一个子目录项如 NameService 都被称做为 znode,这个 znode 是被它所在的路径惟一标识,如 Server1 这个 znode 的标识为 /NameService/Server1;
znode 能够有子节点目录,而且每一个 znode 能够存储数据,注意 EPHEMERAL 类型的目录节点不能有子节点目录
;
znode 是有版本的,每一个 znode 中存储的数据能够有多个版本,也就是一个访问路径中能够存储多份数据
;
znode 能够是临时节点,一旦建立这个 znode 的客户端与服务器失去联系,这个 znode 也将自动删除
,Zookeeper 的客户端和服务器通讯采用长链接方式,每一个客户端和服务器经过心跳来保持链接
,这个链接状态称为 session,若是 znode 是临时节点,这个 session 失效,znode 也就删除了;
znode 的目录名能够自动编号
,如 App1 已经存在,再建立的话,将会自动命名为 App2;
znode 能够被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等
,一旦变化能够通知设置监控的客户端,这个是 Zookeeper 的核心特性
,Zookeeper 的不少功能都是基于这个特性实现的,后面在典型的应用场景中会有实例介绍;
znode节点的状态信息:
使用get命令获取指定节点的数据时, 同时也将返回该节点的状态信息, 称为Stat
. 其包含以下字段:
czxid. 节点建立时的zxid; mzxid. 节点最新一次更新发生时的zxid; ctime. 节点建立时的时间戳; mtime. 节点最新一次更新发生时的时间戳; dataVersion. 节点数据的更新次数; cversion. 其子节点的更新次数; aclVersion. 节点ACL(受权信息)的更新次数; ephemeralOwner. 若是该节点为ephemeral节点, ephemeralOwner值表示与该节点绑定的session id. 若是该节点不是ephemeral节点, ephemeralOwner值为0. 至于什么是ephemeral节点; dataLength. 节点数据的字节数; numChildren. 子节点个数;
zxid:
znode节点的状态信息中包含czxid和mzxid, 那么什么是zxid呢?
ZooKeeper状态的每一次改变, 都对应着一个递增的Transaction id, 该id称为zxid
. 因为zxid的递增性质, 若是zxid1小于zxid2, 那么zxid1确定先于zxid2发生. 建立任意节点, 或者更新任意节点的数据, 或者删除任意节点, 都会致使Zookeeper状态发生改变, 从而致使zxid的值增长
.
session:
在client和server通讯以前, 首先须要创建链接, 该链接称为session. 链接创建后, 若是发生链接超时, 受权失败, 或者显式关闭链接, 链接便处于CLOSED状态, 此时session结束.
节点类型:
讲述节点状态的ephemeralOwner字段时, 提到过有的节点是ephemeral节点, 而有的并非. 那么节点都具备哪些类型呢? 每种类型的节点又具备哪些特色呢?
persistent. persistent节点不和特定的session绑定
, 不会随着建立该节点的session的结束而消失, 而是一直存在, 除非该节点被显式删除.
ephemeral. ephemeral节点是临时性的, 若是建立该节点的session结束了, 该节点就会被自动删除
. ephemeral节点不能拥有子节点
. 虽然ephemeral节点与建立它的session绑定, 但只要该该节点没有被删除, 其余session就能够读写该节点中关联的数据. 使用-e参数指定建立ephemeral节点
.
create -e /xing/ei world
sequence. 严格的说, sequence并不是节点类型中的一种
. sequence节点既能够是ephemeral的, 也能够是persistent的. 建立sequence节点时, ZooKeeper server会在指定的节点名称后加上一个数字序列, 该数字序列是递增的
. 所以能够屡次建立相同的sequence节点, 而获得不一样的节点. 使用-s参数指定建立sequence节点
.
[zk: localhost:4180(CONNECTED) 0] create -s /xing/item world Created /xing/item0000000001 [zk: localhost:4180(CONNECTED) 1] create -s /xing/item world Created /xing/item0000000002 [zk: localhost:4180(CONNECTED) 2] create -s /xing/item world Created /xing/item0000000003 [zk: localhost:4180(CONNECTED) 3] create -s /xing/item world Created /xing/item0000000004
watch:
watch的意思是监听感兴趣的事件
. 在命令行中, 如下几个命令能够指定是否监听相应的事件.
ls命令. ls命令的第一个参数指定znode, 第二个参数若是为true, 则说明监听该znode的子节点的增减, 以及该znode自己的删除事件.
[zk: localhost:4180(CONNECTED) 21] ls /xing true [] [zk: localhost:4180(CONNECTED) 22] create /xing/item item000 WATCHER:: WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/xing Created /xing/item
get命令. get命令的第一个参数指定znode, 第二个参数若是为true, 则说明监听该znode的更新和删除事件
.
[zk: localhost:4180(CONNECTED) 39] get /xing true world cZxid = 0x100000066 ctime = Fri May 17 22:30:01 CST 2013 mZxid = 0x100000066 mtime = Fri May 17 22:30:01 CST 2013 pZxid = 0x100000066 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 5 numChildren = 0 [zk: localhost:4180(CONNECTED) 40] create /xing/item item000 Created /xing/item [zk: localhost:4180(CONNECTED) 41] rmr /xing WATCHER:: WatchedEvent state:SyncConnected type:NodeDeleted path:/xing
stat命令. stat命令用于获取znode的状态信息. 第一个参数指定znode, 若是第二个参数为true, 则监听该node的更新和删除事件.
#2 如何使用Zookeeper# Zookeeper 做为一个分布式的服务框架,主要用来解决分布式集群中应用系统的一致性问题
,它能提供基于相似于文件系统的目录节点树方式的数据存储,可是 Zookeeper 并非用来专门存储数据的,它的做用主要是用来维护和监控你存储的数据的状态变化
。经过监控这些数据状态的变化,从而能够达到基于数据的集群管理
,后面将会详细介绍 Zookeeper 可以解决的一些典型问题,这里先介绍一下,Zookeeper 的操做接口和简单使用示例。
##2.1 经常使用接口操做## 客户端要链接 Zookeeper 服务器能够经过建立 org.apache.zookeeper.ZooKeeper
的一个实例对象,而后调用这个类提供的接口来和服务器交互。
前面说了 ZooKeeper 主要是用来维护和监控一个目录节点树中存储的数据的状态
,全部咱们可以操做 ZooKeeper 的也和操做目录节点树大致同样,如建立一个目录节点,给某个目录节点设置数据,获取某个目录节点的全部子目录节点,给某个目录节点设置权限和监控这个目录节点的状态变化。
ZooKeeper 基本的操做示例:
public class ZkDemo { public static void main(String[] args) throws IOException, KeeperException, InterruptedException { // 建立一个与服务器的链接 ZooKeeper zk = new ZooKeeper("127.0.0.1:2180", 60000, new Watcher() { // 监控全部被触发的事件 // 当对目录节点监控状态打开时,一旦目录节点的状态发生变化,Watcher 对象的 process 方法就会被调用。 public void process(WatchedEvent event) { System.out.println("EVENT:" + event.getType()); } }); // 查看根节点 // 获取指定 path 下的全部子目录节点,一样 getChildren方法也有一个重载方法能够设置特定的 watcher 监控子节点的状态 System.out.println("ls / => " + zk.getChildren("/", true)); // 判断某个 path 是否存在,并设置是否监控这个目录节点,这里的 watcher 是在建立 ZooKeeper 实例时指定的 watcher; // exists方法还有一个重载方法,能够指定特定的 watcher if (zk.exists("/node", true) == null) { // 建立一个给定的目录节点 path, 并给它设置数据; // CreateMode 标识有四种形式的目录节点,分别是: // PERSISTENT:持久化目录节点,这个目录节点存储的数据不会丢失; // PERSISTENT_SEQUENTIAL:顺序自动编号的目录节点,这种目录节点会根据当前已近存在的节点数自动加 1,而后返回给客户端已经成功建立的目录节点名; // EPHEMERAL:临时目录节点,一旦建立这个节点的客户端与服务器端口也就是 session 超时,这种节点会被自动删除; // EPHEMERAL_SEQUENTIAL:临时自动编号节点 zk.create("/node", "conan".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); System.out.println("create /node conan"); // 查看/node节点数据 System.out.println("get /node => " + new String(zk.getData("/node", false, null))); // 查看根节点 System.out.println("ls / => " + zk.getChildren("/", true)); } // 建立一个子目录节点 if (zk.exists("/node/sub1", true) == null) { zk.create("/node/sub1", "sub1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); System.out.println("create /node/sub1 sub1"); // 查看node节点 System.out.println("ls /node => " + zk.getChildren("/node", true)); } // 修改节点数据 if (zk.exists("/node", true) != null) { // 给 path 设置数据,能够指定这个数据的版本号,若是 version 为 -1 怎能够匹配任何版本 zk.setData("/node", "changed".getBytes(), -1); // 查看/node节点数据 // 获取这个 path 对应的目录节点存储的数据,数据的版本等信息能够经过 stat 来指定,同时还能够设置是否监控这个目录节点数据的状态 System.out.println("get /node => " + new String(zk.getData("/node", false, null))); } // 删除节点 if (zk.exists("/node/sub1", true) != null) { // 删除 path 对应的目录节点,version 为 -1 能够匹配任何版本,也就删除了这个目录节点全部数据 zk.delete("/node/sub1", -1); zk.delete("/node", -1); // 查看根节点 System.out.println("ls / => " + zk.getChildren("/", true)); } // 关闭链接 zk.close(); } }
#3 ZooKeeper 典型的应用场景# Zookeeper 从设计模式角度来看,是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理你们都关心的数据,而后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper 就将负责通知已经在 Zookeeper 上注册的那些观察者作出相应的反应,从而实现集群中相似 Master/Slave 管理模式
,关于 Zookeeper 的详细架构等内部细节能够阅读 Zookeeper 的源码。
下面详细介绍这些典型的应用场景,也就是 Zookeeper 到底能帮咱们解决那些问题?下面将给出答案。
##3.1 统一命名服务(Name Service)## 分布式应用中,一般须要有一套完整的命名规则,既可以产生惟一的名称又便于人识别和记住,一般状况下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构,既对人友好又不会重复
。说到这里你可能想到了 JNDI,没错 Zookeeper 的 Name Service 与 JNDI 可以完成的功能是差很少的,它们都是将有层次的目录结构关联到必定资源上,可是 Zookeeper 的 Name Service 更加是普遍意义上的关联,也许你并不须要将名称关联到特定资源上,你可能只须要一个不会重复名称,就像数据库中产生一个惟一的数字主键同样。
Name Service 已是 Zookeeper 内置的功能
,你只要调用 Zookeeper 的 API 就能实现。如调用 create 接口就能够很容易建立一个目录节点。
命名服务也是分布式系统中比较常见的一类场景。在分布式系统中,经过使用命名服务,客户端应用可以根据指定名字来获取资源或服务的地址,提供者等信息
。被命名的实体一般能够是集群中的机器,提供的服务地址,远程对象等等——这些咱们均可以统称他们为名字(Name)
。其中较为常见的就是一些分布式服务框架中的服务地址列表。经过调用ZK提供的建立节点的API,可以很容易建立一个全局惟一的path,这个path就能够做为一个名称。
命名服务实例:
阿里巴巴集团开源的分布式服务框架Dubbo中使用ZooKeeper来做为其命名服务,维护全局的服务地址列表,在Dubbo实现中:
服务提供者在启动的时候
,向ZK上的指定节点/dubbo/${serviceName}/providers
目录下写入本身的URL地址,这个操做就完成了服务的发布。
服务消费者启动的时候
,订阅/dubbo/${serviceName}/providers
目录下的提供者URL地址, 并向/dubbo/${serviceName} /consumers
目录下写入本身的URL地址。
注意,全部向ZK上注册的地址都是临时节点,这样就可以保证服务提供者和消费者可以自动感应资源的变化
。 另外,Dubbo还有针对服务粒度的监控,方法是订阅/dubbo/${serviceName}目录下全部提供者和消费者的信息
。
##3.2 配置管理(Configuration Management)## 配置的管理在分布式应用环境中很常见,例如同一个应用系统须要多台 PC Server 运行,可是它们运行的应用系统的某些配置项是相同的,若是要修改这些相同的配置项,那么就必须同时修改每台运行这个应用系统的 PC Server,这样很是麻烦并且容易出错。
像这样的配置信息彻底能够交给 Zookeeper 来管理,将配置信息保存在 Zookeeper 的某个目录节点中,而后将全部须要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到 Zookeeper 的通知,而后从 Zookeeper 获取新的配置信息应用到系统中
。
发布与订阅模型,即所谓的配置中心
,顾名思义就是发布者将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新
。例如全局的配置信息,服务式服务框架的服务地址列表等就很是适合使用。
配置管理实例:
应用中用到的一些配置信息放到ZK上进行集中管理
。这类场景一般是这样:应用在启动的时候会主动来获取一次配置
,同时,在节点上注册一个Watcher
,这样一来,之后每次配置有更新的时候,都会实时通知到订阅的客户端,历来达到获取最新配置信息的目的。
分布式搜索服务中
,索引的元信息和服务器集群机器的节点状态存放在ZK的一些指定节点,供各个客户端订阅使用。
分布式日志收集系统
。这个系统的核心工做是收集分布在不一样机器的日志。收集器一般是按照应用来分配收集任务单元,所以须要在ZK上建立一个以应用名做为path的节点P,并将这个应用的全部机器ip,以子节点的形式注册到节点P上,这样一来就可以实现机器变更的时候,可以实时通知到收集器调整任务分配。
系统中有些信息须要动态获取,而且还会存在人工手动去修改这个信息的发问
。一般是暴露出接口,例如JMX接口,来获取一些运行时的信息。引入ZK以后,就不用本身实现一套方案了,只要将这些信息存放到指定的ZK节点上便可。
注意:在上面提到的应用场景中,有个默认前提是:数据量很小,可是数据更新可能会比较快的场景
。
##3.3 集群管理(Group Membership)## Zookeeper 可以很容易的实现集群管理的功能,若有多台 Server 组成一个服务集群,那么必需要一个“总管”知道当前集群中每台机器的服务状态,一旦有机器不能提供服务,集群中其它集群必须知道
,从而作出调整从新分配服务策略。一样当增长集群的服务能力时,就会增长一台或多台 Server,一样也必须让“总管”知道
。
Zookeeper 不只可以帮你维护当前的集群中机器的服务状态,并且可以帮你选出一个“总管”,让这个总管来管理集群,这就是 Zookeeper 的另外一个功能 Leader Election
。
它们的实现方式都是在 Zookeeper 上建立一个 EPHEMERAL 类型的目录节点
,而后每一个 Server 在它们建立目录节点的父目录节点上调用 getChildren(String path, boolean watch) 方法并设置 watch 为 true
,因为是 EPHEMERAL 目录节点,当建立它的 Server 死去,这个目录节点也随之被删除,因此 Children 将会变化,这时 getChildren上的 Watch 将会被调用,因此其它 Server 就知道已经有某台 Server 死去了。新增 Server 也是一样的原理。
Zookeeper 如何实现 Leader Election,也就是选出一个 Master Server。和前面的同样每台 Server 建立一个 EPHEMERAL 目录节点,不一样的是它仍是一个 SEQUENTIAL 目录节点,因此它是个 EPHEMERAL_SEQUENTIAL 目录节点
。之因此它是 EPHEMERAL_SEQUENTIAL 目录节点,是由于咱们能够给每台 Server 编号,咱们能够选择当前是最小编号的 Server 为 Master
,假如这个最小编号的 Server 死去,因为是 EPHEMERAL 节点,死去的 Server 对应的节点也被删除,因此当前的节点列表中又出现一个最小编号的节点,咱们就选择这个节点为当前 Master
。这样就实现了动态选择 Master,避免了传统意义上单 Master 容易出现单点故障的问题。
1. 集群机器监控
这一般用于那种对集群中机器状态,机器在线率有较高要求的场景
,可以快速对集群中机器变化做出响应。这样的场景中,每每有一个监控系统,实时检测集群机器是否存活。过去的作法一般是:监控系统经过某种手段(好比ping)定时检测每一个机器,或者每一个机器本身定时向监控系统汇报“我还活着”。 这种作法可行,可是存在两个比较明显的问题:
利用ZooKeeper有两个特性,就能够实现另外一种集群机器存活性监控系统:
客户端在节点 x 上注册一个Watcher,那么若是 x 的子节点变化了,会通知该客户端
。建立EPHEMERAL类型的节点,一旦客户端和服务器的会话结束或过时,那么该节点就会消失
。例如
:监控系统在 /clusterServers 节点上注册一个Watcher,之后每动态加机器,那么就往 /clusterServers 下建立一个 EPHEMERAL类型的节点:/clusterServers/{hostname}. 这样,监控系统就可以实时知道机器的增减状况,至于后续处理就是监控系统的业务了。
2. Master选举则是zookeeper中最为经典的应用场景了
在分布式环境中,相同的业务应用分布在不一样的机器上,有些业务逻辑(例如一些耗时的计算,网络I/O处理),每每只须要让整个集群中的某一台机器进行执行,其他机器能够共享这个结果
,这样能够大大减小重复劳动,提升性能,因而这个master选举即是这种场景下的碰到的主要问题
。
利用ZooKeeper的强一致性,可以保证在分布式高并发状况下节点建立的全局惟一性
,即:同时有多个客户端请求建立 /currentMaster 节点,最终必定只有一个客户端请求可以建立成功。利用这个特性,就能很轻易的在分布式环境中进行集群选取了。
另外,这种场景演化一下,就是动态Master选举
。这就要用到EPHEMERAL_SEQUENTIAL类型节点的特性了
。
上文中提到,全部客户端建立请求,最终只有一个可以建立成功。在这里稍微变化下,就是**容许全部请求都可以建立成功,可是得有个建立顺序
**,因而全部的请求最终在ZK上建立结果的一种可能状况是这样: /currentMaster/{sessionId}-1 ,/currentMaster/{sessionId}-2,/currentMaster/{sessionId}-3 ….. 每次选取序列号最小的那个机器做为Master,若是这个机器挂了,因为他建立的节点会立刻消失,那么以后最小的那个机器就是Master了
。
**3. 在搜索系统中,若是集群中每一个机器都生成一份全量索引,不只耗时,并且不能保证彼此之间索引数据一致。**所以让集群中的Master来进行全量索引的生成,而后同步到集群中其它机器。另外,Master选举的容灾措施是,能够随时进行手动指定master,就是说应用在zk在没法获取master信息时,能够经过好比http方式,向一个地方获取master。
**4. 在Hbase中,也是使用ZooKeeper来实现动态HMaster的选举。**在Hbase实现中,会在ZK上存储一些ROOT表的地址和HMaster的地址,HRegionServer也会把本身以临时节点(Ephemeral)的方式注册到Zookeeper中,使得HMaster能够随时感知到各个HRegionServer的存活状态,同时,一旦HMaster出现问题,会从新选举出一个HMaster来运行,从而避免了HMaster的单点问题。
##3.4 共享锁(Locks)## 共享锁在同一个进程中很容易实现,可是在跨进程或者在不一样 Server 之间就很差实现了。Zookeeper 却很容易实现这个功能,实现方式也是须要得到锁的 Server 建立一个 EPHEMERAL_SEQUENTIAL 目录节点,而后调用 getChildren方法获取当前的目录节点列表中最小的目录节点是否是就是本身建立的目录节点,若是正是本身建立的,那么它就得到了这个锁,若是不是那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到本身建立的节点是列表中最小编号的目录节点,从而得到锁,释放锁很简单,只要删除前面它本身所建立的目录节点就好了
。
分布式锁,这个主要得益于ZooKeeper为咱们保证了数据的强一致性。锁服务能够分为两类,一个是保持独占,另外一个是控制时序
。
所谓保持独占,就是全部试图来获取这个锁的客户端,最终只有一个能够成功得到这把锁
。一般的作法是把zk上的一个znode看做是一把锁,经过create znode的方式来实现。全部客户端都去建立 /distribute_lock 节点,最终成功建立的那个客户端也即拥有了这把锁
。
控制时序,就是全部视图来获取这个锁的客户端,最终都是会被安排执行,只是有个全局时序了
。作法和上面基本相似,只是这里 /distribute_lock 已经预先存在,客户端在它下面建立临时有序节点(这个能够经过节点的属性控制:CreateMode.EPHEMERAL_SEQUENTIAL来指定)。Zk的父节点(/distribute_lock)维持一份sequence,保证子节点建立的时序性,从而也造成了每一个客户端的全局时序。
##3.5 队列管理## Zookeeper 能够处理两种类型的队列:
当一个队列的成员都聚齐时,这个队列才可用,不然一直等待全部成员到达
,这种是同步队列。
队列按照 FIFO 方式进行入队和出队操做
,例如实现生产者和消费者模型。
同步队列用 Zookeeper 实现的实现思路以下:
建立一个父目录 /synchronizing,每一个成员都监控标志(Set Watch)位目录 /synchronizing/start 是否存在,而后每一个成员都加入这个队列,加入队列的方式就是建立 /synchronizing/member_i 的临时目录节点,而后每一个成员获取 / synchronizing 目录的全部目录节点,也就是 member_i。判断 i 的值是否已是成员的个数,若是小于成员个数等待 /synchronizing/start 的出现,若是已经相等就建立 /synchronizing/start
。
FIFO 队列用 Zookeeper 实现思路以下:
实现的思路也很是简单,就是在特定的目录下建立 SEQUENTIAL 类型的子目录 /queue_i,这样就能保证全部成员加入队列时都是有编号的
,出队列时经过 getChildren( ) 方法能够返回当前全部的队列中的元素,而后消费其中最小的一个,这样就能保证 FIFO。
##3.6 负载均衡## 这里说的负载均衡是指软负载均衡
。在分布式环境中,为了保证高可用性,一般同一个应用或同一个服务的提供方都会部署多份,达到对等服务。而消费者就需要在这些对等的服务器中选择一个来执行相关的业务逻辑,其中比较典型的是消息中间件中的生产者,消费者负载均衡
。
消息中间件中发布者和订阅者的负载均衡
,linkedin开源的KafkaMQ和阿里开源的metaq都是经过zookeeper来作到生产者、消费者的负载均衡
。这里以metaq为例如讲下:
生产者负载均衡
:metaq发送消息的时候,生产者在发送消息的时候必须选择一台broker上的一个分区来发送消息,所以metaq在运行过程当中,会把全部broker和对应的分区信息所有注册到ZK指定节点上,默认的策略是一个依次轮询的过程,生产者在经过ZK获取分区列表以后,会按照brokerId和partition的顺序排列组织成一个有序的分区列表,发送的时候按照从头至尾循环往复的方式选择一个分区来发送消息。
消费负载均衡
: 在消费过程当中,一个消费者会消费一个或多个分区中的消息,可是一个分区只会由一个消费者来消费。MetaQ的消费策略是:
在某个消费者故障或者重启等状况下,其余消费者会感知到这一变化(经过 zookeeper watch消费者列表),而后从新进行负载均衡,保证全部的分区都有消费者进行消费。
##3.7 分布式通知/协调## ZooKeeper中特有watcher注册与异步通知机制,可以很好的实现分布式环境下不一样系统之间的通知与协调,实现对数据变动的实时处理
。使用方法一般是不一样系统都对ZK上同一个znode进行注册,监听znode的变化(包括znode自己内容及子节点的),其中一个系统update了znode,那么另外一个系统可以收到通知,并做出相应处理。
另外一种心跳检测机制
:检测系统和被检测系统之间并不直接关联起来,而是经过zk上某个节点关联,大大减小系统耦合。另外一种系统调度模式
:某系统有控制台和推送系统两部分组成,控制台的职责是控制推送系统进行相应的推送工做。管理人员在控制台做的一些操做,其实是修改了ZK上某些节点的状态,而ZK就把这些变化通知给他们注册Watcher的客户端,即推送系统,因而,做出相应的推送任务。另外一种工做汇报模式
:一些相似于任务分发系统,子任务启动后,到zk来注册一个临时节点,而且定时将本身的进度进行汇报(将进度写回这个临时节点),这样任务管理者就可以实时知道任务进度。总之,使用zookeeper来进行分布式通知和协调可以大大下降系统之间的耦合。