前言
本文隶属于专栏《1000个问题搞定大数据技术体系》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!
本专栏目录结构和参考文献请见1000个问题搞定大数据技术体系
正文
数据发布与订阅
数据发布与订阅模型,即所谓的全局配置中心,就是发布者将需要全局统一管理的数据发布到 ZooKeeper 节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新。
例如全局的配置信息,服务式服务框架的服务地址列表等就非常适合使用。
接下来,介绍一些数据发布与订阅的主要应用场景
- 应用中用到的一些配置信息放到 ZooKeeper 上进行集中管理。
这类场景通常是这样:应用在启动时会主动来获取一次配置,同时,在节点上注册一个 Watcher ,这样一来,以后每次配置有更新的时候,都会实时通知到订阅的客户端,用来达到获取最新配置信息的目的。 - 分布式搜索服务中,索引的元信息和服务器集群机器的节点状态存放在 ZooKeeper 的一些指定节点,供各个客户端订阅使用。
- 分布式日志收集系统中,这个系统的核心工作是收集分布在不同机器的日志。
收集器通常是按照应用来分配收集任务单元,因此需要在 ZooKeeper 上创建一个以应用名作为 path 的节点 P ,并将这个应用的所有机器 IP ,以子节点的形式注册到节点 P 上,这样一来当机器变动的时候,能够实时通知到收集器调整任务分配。 - 系统中有些信息需要动态获取,并且还会存在人工手动去修改这个信息的发问通常是暴露出接口,例如 JMX 接口,来获取一些运行时的信息。
引人 ZooKeeper 之后就不用自己实现一套方案了,只要将这些信息存放到指定的 ZooKeeper 节点上即可。
小提示:在上面提到的应用场景中,有个默认前提是:数据量很小,但是数据更新可能会比较快的场景。
统一命名服务
命名服务也是分布式系统中比较常见的一类场景。
在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源服务的地址,提供者等信息。
被命名的实体通常可以是集群中的机器,提供的服务地址,进程对象等,这些都可以统称为名字( Name )。
其中较为常见的就是一些分布式服务框架中的服务地址列表。
通过调用 ZooKeeper 提供的创建节点的 API ,能够很容易创建一个全局唯一的 path ,这个 path 就可以作为一个名称。
接下来,介绍统一命名服务的主要应用场景
阿里开源的分布式服务框架 Dubbo 中使用 ZooKeeper 来作为其命名服务,维护全局的服务地址列表。
在 Dubbo 实现中:
服务提供者在启动的时候,向 ZooKeeper 上的指定节点/dubbo/${serviceName}/providers 目录下写入自己的 URL 地址,这个操作就完成了服务的发布。
服务消费者启动的时候,订阅 /dubbo/ ${serviceName}/providers 目录下的提供者 URL 地址,并向/dubbo/ ${serviceName}/consumers 目录下写入自己的 URL 地址。
小提示:
(1) 所有向 ZooKeeper 上注册的地址都是临时节点,这样能够保证服务提供者和消费者能够自动感应资源的变化。
(2) Dubbo 还有针对服务粒度的监控,方法是訂阅/dubbo/${serviceName} 目录下所有提供者和消费者的信息。
分布式锁
分布式锁,主要得益于 ZooKeeper 保证了数据的强一致性。
锁服务可以分为两类,一个是保持独占,另一个是控制时序。
所谓保持独占,就是将所有试图来获取这个锁的客户端,最终只有一个客户端可以成功获得这把锁,从而执行相应操作(通常的做法是把 ZooKeeper 上的一个 Znode 看作是一把锁,通过创建临时节点的方式来实现);
控制时序则是所有试图来获取锁的客户端,最终都会被执行,只是存在了全局时序。
它的实现方法和保持独占基本类似,这里/distribute_lock 预先存在,那么客户端在它下面创建临时序列化节点
(这个可以通过节点的属性控制: CreateMode.EPHEMERAL_SEQUENTIAL 来指定),并根据序列号大小进行时序性操作。
接下来,介绍分布式锁的主要应用场景。
当所有客户端都去创建/distribute_lock 临时非序列化节点,那么最终成功创建的客户端也即拥有了这把锁,拥有了访问该数据的权限,当操作完毕后,断开与 ZooKeeper 连接,那么该临时节点就会被删除,如果其他客户端需要操作这个文件,客户端只需监听这个目录是否存在即可。
关于用 Zookeeper 实现分布式锁的更多内容请参考我的这篇博客——如何用 ZooKeeper 实现分布式锁?