集群管理
1 需求
对于集群,总是希望能够随时获取到以下信息:
- 当前集群中各个主机的运行时状态
- 当前集群中主机的存活状况
2 基本原理
3 扩展
该功能使用Agent也可以完成,其与使用zk不同的地方是,对于集群中主机的存活状态,使用zk可以做到实时监控。
4 分布式日志收集系统
以分布式日志收集系统为例来分析zk对于集群的管理。
(1) 系统组成
首先要清楚,分布式日志收集系统由四部分组成:日志源集群、日志收集器集群,zk集群,及监控系统。
(2) 系统工作原理
分布式日志收集系统的工作步骤有以下几步:
A、收集器的注册
在zk上创建各个收集器对应的节点。例如/logs/collector1
具体实现步骤:
- 在系统启动时首先创建收集器根节点/logs
- 收集器逐个启动就会逐个在/logs下创建对应的永久节点例如/logs/collector1、
B、任务分配
系统根据收集器的个数,将所有日志源集群主机分组,分别分配给各个收集器。
- 日志源主机会在为其分配的收集器节点下创建临时节点,一个日志源主机一个节点
C、状态收集
这里的状态收集指的是两方面的收集:
- 日志源主机状态,例如,日志源主机是否存活,其已经产生多少日志等
- 收集器的运行状态,例如,收集器本身已经收集了多少字节的日志、当前CPU、内存的使用情况等
收集器/日志源主机会定时/实时将其自身状态写入到对应的节点内容中。
D、任务再分配Rebalance
当出现收集器挂掉或扩容,就需要动态地进行日志收集任务再分配了,这个过程称为Rebalance。
存在两种再分配方案:
- 全局动态分配:不推荐使用,系统耗损。
- 局部动态分配:首先要定义好收集器的负载判别标准。
分布式锁
分布式锁是控制分布式系统同步访问共享资源的一种方式,zookeeper可以实现分布式锁功能。根据用户操作类型的不同,可以分为排他锁与共享锁。
- 排他锁:写锁
- 共享锁:读锁
1 分布式锁的实现
在zk上对于分布式锁的实现,使用的是类似于“/xs_lock/[hostname]-请求类型-序号”的临时顺序节点。
- Step1:当一个客户端向某资源发出读/写操作请求时,该客户端首先会对/xs_lock节点注册子节点列表变更事件的watcher监听,随时监听子节点的变化情况。
- Step2: watcher注册完毕后,其会在/xs_lock节点下创建一个读写操作的临时顺序节点。读写操作的顺序性就是通过这些子节点的顺序性体现的。注意,读写操作所创建的节点名称是不同的。
- Step3:节点创建完后,其就会触发客户端的watcher回调,读取/xs_lock节点下的所有子节点列表,然后比较当前子节点与其它子节点序号的大小关系,并根据读写操作的不同,执行不同的逻辑。
- 读请求:若没有比自己小的节点,或比自己小的节点都是读请求节点,则当前请求可以直接读取;若比自己小的节点中存在写请求节点,则当前请求等待。
- 写请求:若没有比自己小的节点,则直接进行写操作。若发现有比自己小的节点,那些节点无论是读还是写节点,当前写操作都需要等待。
- Step4:客户端操作完毕后,与zk的连接断开,则zk中该会话对应的节点消失。
2 分布式锁的改进
前面的实现方式存在“羊群效应”,为了解决其所带来的性能下降,可以对前述分布式锁的实现进行改进;由于一个操作而引发了大量的低效或无用的操作的执行,这种情况称为羊群效应。
当客户端请求发出后,在zk中创建相应的临时顺序节点后马上触发watcher机制获取当前的/xs_lock的所有子节点列表,但任何客户端都不向/xs_lock注册用于监听子节点列表变化的watcher,而是改为根据请求类型的不同向“对其有影响的”子节点注册watcher。
- 读请求:只需要关注比其序号小的最后一个写节点
- 写请求:仅关注比自己小的最后一个节点,无论是读节点还是写节点
分布式队列
zk可以实现简单的消息队列(实际生产中不会应用)。
1 FIFO队列
zk实现FIFO队列的思路是:利用顺序节点的有序性,为每个数据在zk中都创建一个相应的节点。然后为每个节点都注册watcher监听。一个节点被消费,则会引发消费者消费下一个节点,直到消费完毕。
2 分布式屏障Barrier队列(实际生产会应用)
Barrier,屏障、障碍物。Barrier队列是分布式系统中的一种同步协调器,规定了一个队列中的元素必须全部聚齐后才能继续执行后面的任务,否则一直等待。其常见于大规模分布式并行计算的应用场景中:最终的合并计算需要基于很多并行计算的子结果来进行。
zk对于Barrier的实现原理是,在zk中创建一个/barrier节点,其数据内容设置为屏障打开的阈值,即当其下的子节点数量达到该阈值后,app才可进行最终的计算,否则一直等待。每一个并行运算完成,都会在/barrier下创建一个子节点,直到所有并行运算完成。