【分布式解决方案】ZooKeeper经典应用场景

1,261 阅读7分钟

Zookeeper能做什么

你们还记得上一篇文章吗?

juejin.cn/post/684490…

本篇将根据ZooKeeper的特征进行经典应用场景的分析。

1、配置管理

配置管理又被称为发布-订阅

【实现方式】

数据发布者将数据发布(保存)到Zookeeper的节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新。

发布/订阅系统一般有两种设计模式,分别是推(Push)和拉(Pull)模式。

  • 推模式

服务端主动将数据更新发送给所有订阅的客户端

  • 拉模式

客户端主动发起请求来获取最新数据,通常客户端都采用定时轮询拉取的方式

ZooKeeper 采用的是推拉相结合的方式

客户端向服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应 的客户端发送Watcher事件通知,客户端接收到这个消息通知后,需要主动到服务端获取最新的数据

【实现用例】

可参考:blog.csdn.net/java_66666/…

2、命名服务

命名服务也是分布式系统中比较常见的一类场景。在分布式系统中,通过使用命名服务,客户端 应用能够根据指定名字来获取资源或服务的地址,提供者等信息。被命名的实体通常可以是集群中的 机器,提供的服务,远程对象等等——这些我们都可以统称他们为名字。

其中较为常见的就是一些分布式服务框架(如RPC)中的服务地址列表。通过在ZooKeepr里 创建顺序节点,能够很容易创建一个全局唯一的路径,这个路径就可以作为一个名字。

ZooKeeper 的命名服务即生成全局唯一的ID。

【实现用例】

Dobbu注册中心的实现

3、分布式协调服务/通知

ZooKeeper 中特有 Watcher 注册与异步通知机制,能够很好的实现分布式环境下不同机器, 甚至不同系统之间的通知与协调,从而实现对数据变更的实时处理。

使用方法通常是不同的客户端 如果机器节点发生了变化,那么所有订阅的客户端都能够接收到相应的Watcher通知,并做出相应 的处理。

这种协调方式和通过ZooKeeper实现集群监控的方式很类似,将在集群管理中详细讲解。

ZooKeeper的分布式协调/通知,是一种通用的分布式系统机器间的通信方式。

4、集群管理

4.1、集群机器监控

对于集群中机器状态的监控,可以通过zookeeper来完成,先来看看传统的集群管理方式:

建通系统通过某种手段(比如心跳)定时检测每个节点,或者每个节点自己定时向master汇报当前节点的状态(active)

传统集群管理方式有如下缺点:

  • 将会产生一定的时延(受心跳长短限制);
  • 当集群中的节点发生变更时,其余的节点都需要对维护的集群文件(状态表)进行修改,修改内容多。

使用传统方式进行集群管理需要解决集群状态表的一致性问题,zookeeper提供了良好的解决方案。

【实现方案】

1、在zookeeper中创建一个集群节点; 2、集群中的节点在创建时,作为zookeeper的客户端向zookeeper建链(TCP连接,并维护一个session),并在1中创建的集群节点上注册一个watcher,并将自己的状态注册到集群管理节点上(在集群管理节点上创建一个临时节点); 3、一旦集群节点发生变化,所有注册在zookeeper上的节点都将收到通知,采取拉的方式进行集群状态同步。 4、由于集群中的节点注册为一个临时节点,一旦该节点宕机或者重启,当前节点将会从集群节点中删除,其他节点也能感知到。

4.2、Master选举

Master 选举可以说是 ZooKeeper 最典型的应用场景了。比如 HDFS 中 Active NameNode 的选举、YARN 中 Active ResourceManager 的选举和 HBase 中 Active HMaster 的选举等。

【传统实现方式】

针对 Master 选举的需求,通常情况下,我们可以选择常见的关系型数据库中的主键特性来 实现: 希望成为 Master 的机器都向数据库中插入一条相同主键ID的记录,数据库会帮我们进行 主键冲突检查,也就是说,只有一台机器能插入成功——那么,我们就认为向数据库中成功插入数据 的客户端机器成为Master。

依靠关系型数据库的主键特性确实能够很好地保证在集群中选举出唯一的一个Master。

但是,如果当前选举出的 Master 挂了,那么该如何处理?谁来告诉我 Master 挂了呢? 显然,关系型数据库无法通知我们这个事件。但是,ZooKeeper 可以做到!

【划重点】

Zookeeper为我们保证了数据的强一致性,即:zookeeper集群中任意节点上相同的znode数据一定是相同的。

也就是说,如果同时有多个客户端请求创建同一个临时节点,那么最终一定只有一个客户端 请求能够创建成功。

利用这个特性,就能很容易地在分布式环境中进行 Master 选举了。

成功创建该节点的客户端所在的机器就成为了 Master。同时,其他没有成功创建该节点的 客户端,都会在该节点上注册一个子节点变更的 Watcher,用于监控当前 Master 机器是否存 活,一旦发现当前的Master挂了,那么其他客户端将会重新进行 Master 选举。

这样就实现了 Master 的动态选举。

5、分布式锁

分布式锁是控制分布式系统之间同步访问共享资源的一种方式 分布式锁又分为两类:

  • 独占锁(排他锁)

  • 共享锁

【划重点】Second Times

得益于Zookeeper为我们保证了数据的强一致性:zookeeper集群中任意节点上相同的znode数据一定是相同的,因此我们完全可以依靠zookeeper的数据强一直性来实现分布式锁

5.1、独占锁(排他锁)

所有试图来获取这把锁的客户端,最终只有一个可以成功获得这把锁,我们称之为独占锁。

【实现方案】 通常的做法是:把ZooKeeper上的一个节点看作是一个锁,获得锁就通过创建临时节点(create znode)的方式来实现。 ZooKeeper 会保证在所有客户端中,最终只有一个客户端能够创建成功,那么就可以认为该客户端获得了锁。同时,所有没有获取到锁的客户端就需要到/exclusive_lock节点上注册一个子节点变更的Watcher监听,以便实时监听到lock节点的变更情况。

【释放锁 】 因为锁是一个临时节点,释放锁有两种方式

  • 当前获得锁的客户端机器发生宕机或重启,那么该临时节点就会被删除,释放锁

  • 正常执行完业务逻辑后,客户端就会主动将自己创建的临时节点删除,释放锁。

无论在什么情况下移除了lock节点,ZooKeeper 都会通知所有在 /exclusive_lock 节点上注册了节点变更 Watcher 监听的客户端。这些客户端在接收到通知后,再次重新发起分布式锁获取,即重复『获取锁』过程。

5.2、共享锁

共享锁在同一个进程中很容易实现,但是在跨进程或者在不同 Server 之间就不好实现了。

【实现方案】

1、Zookeeper 却很容易实现这个功能,实现方式也是需要获得锁的 Server 创建一个 EPHEMERAL_SEQUENTIAL 目录节点;

2、然后调用 getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点,如果正是自己创建的,那么它就获得了这个锁,如果不是那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,从而获得锁;

3、释放锁很简单,只要删除前面它自己所创建的目录节点就行了。

原创作品,转载请注明出处。