Zookeeper典型应用场景及实现二

Zookeeper典型应用场景及实现一

5 集群管理

两部分:集群监控、集群控制

使用zookeeper来进行集群管理的好处:
在这里插入图片描述

  • 例子:分布式日志收集系统

设计:需要收集的日志机器(日志源机器)分为多个组别,一个组别对应一个收集器机器,用于收集日志

需要解决的问题:如何快速、合理、动态地为每个收集器分配对应的日志源机器
在这里插入图片描述
1.注册收集器机器:
在ZooKeeper上创建一个节点作为收集器的根节点,例如/logs/collector(下文我们以“收集器节点”,代表该数据节点)
每个收集器机器在启动的时候,都会在收集器节点下创建自己的节点,例如/logs/collector/[Hostname]

2.任务分发

待所有收集器机器都创建好自己对应的节点后,系统根据收集器节点下子节点的个数,将所有日志源机器分成对应的若干组,然后将分组后的机器列表分别写到这些收集器机器创建的子节点(例如/logs/collector/host1)上去。这样一来,每个收集器机器都能够从自己对应的收集器节点上获取日志源机器列表,进而开始进行日志收集工作

3.状态汇报

完成收集器机器的注册以及任务分发后,我们还要考虑到这些机器随时都有挂掉的可能。因此,针对这个问题,我们需要有一个收集器的状态汇报机制:
每个收集器机器在创建完自己的专属节点后,还需要在对应的子节点上创建一个状态子节点,例如/logs/collector/host1/status,每个收集器机器都需要定期向该节点写入自己的状态信息。可以把这种策略看作是一种心跳检测机制,通常收集器机器都会在这个节点中写入日志收集进度信息。日志系统根据该状态子节点的最后更新时间来判断对应的收集器机器是否存活。

4.动态分配

如果收集器机器挂掉或是扩容了,就需要动态的进行收集任务的分配。在运行过程中,日志系统始终关注着/logs/collector这个节点下所有子节点的变更,一旦检测到有收集器机器停止汇报或是有新的收集器机器加入,就要开始任务的重新分配。无论是针对收集器机器停止汇报还是新机器加入的情况,日志系统都需要将之前分配给该收集器的所有任务进行转移。为了解决这个问题,通常有如下两种做法:

4.1 全局动态分配

这是一种简单粗暴的做法,在出现收集器机器挂掉或是新机器加入的时候,日志系统需要根据新的收集器机器列表,立即对所有的日志源机器重新进行一次分组,然后将其分配给剩下的收集器机器。

4.2 局部动态分配

全局动态分配方式虽然策略简单,但是存在一个问题:一个或部分收集器机器的变更,就会导致全局动态任务的分配,影响面比较大,因此风险也就比较大。所谓局部动态分配,顾名思义就是在小范围内进行任务的动态分配。在这种策略中,每个收集器机器在汇报自己日志收集状态的同时,也会把自己的负载汇报上去,请注意,这里提到的负载并不仅仅只是简单地指机器CPU负载(Load),而是一个对当前收集器任务执行的综合评估,这个评估算法和ZooKeeper本身并没有太大的关系,这里不再赘述。

在这种策略中,如果一个收集器机器挂了,那么日志系统就会把之前分配给这个机器的任务重新分配到那些负载较低的机器上去。同样,如果有新的收集器机器加入,会从那些负载高的机器上转移部分任务给这个新加入的机器。

5 注意事项

在上面的介绍中,我们已经了解了ZooKeeper是如何协调一个分布式日志收集系统工作的,接下来再来看看一些细节问题。

5.1 节点类型的选择

我们首先来看/logs/collector这个节点下面子节点的节点类型。
/logs/collector节点下面的所有子节点都代表了每个收集器机器,那么初步认为这些子节点必须选择临时节点,原因是日志系统可以根据这些临时节点来判断收集器机器的存活性。
但是,同时还需要注意的一点是:在分布式日志收集这个场景中,收集器节点上还会存放所有已经分配给该收集器机器的日志源机器列表,如果只是简单地依靠ZooKeeper自身的临时节点机制,那么当一个收集器机器挂掉或是当这个收集器机器中断“心跳汇报”的时候,待该收集器节点的会话失效后,ZooKeeper就会立即删除该节点,又是,记录在该节点上的所有日志源机器列表也就随之被清除掉了。

因此,临时节点显然无法满足这里的业务需求,所以使用持久节点来标识每一个收集器机器,同时在这个持久节点下面分别创建/logs/collector/[Hostname]/status临时节点来表征每一个收集器机器的状态。
这样一来,既能实现日志系统对所有收集器的监控,同时在收集器机器挂掉后,依然能够准确的将分配于其中的任务还原。

5.2 日志系统节点监听方案的选择

在实际生产运行过程中,每一个收集器机器更改自己状态节点的频率可能非常高(如每秒1次或更短),而且收集器的数量可能非常大,如果日志系统监听所有这些节点变化,那么通知的消息量可能会非常大。
另一方面,在收集器机器正常工作的情况下,日志系统没有必要去实时的接收每次节点状态变更,因此大部分这些状态变更通知都是无用的。
因此我们考虑放弃监听设置,而是采用日志系统主动轮询收集器节点的策略,这样就节省了不少网卡流量,唯一的缺陷就是有一定的延时(考虑到分布式日志收集系统的定位,这个延时是可以接受的)。

  • 例子:在线云主机管理

问题:
在这里插入图片描述
1.机器上下线

通过创建和删除临时节点来完成

2.机器监控

Agent定时将主机的运行状态信息写入zookeeper,通过订阅节点来更新主机的信息

6 Master选举

关系型数据库的主键模型:
在这里插入图片描述
缺点是master挂了后,集群无法得知,因为没有通知机制

zookeeper的选举:
在这里插入图片描述
可以达到动态选举的目的

7 分布式锁

控制分布式系统之间同步访问共享资源的一种方式

  • 排他锁

含义:保证当前有且仅有一个事务获取锁,且锁被释放后,所有正在等待获取锁的事务都能够被通知到;即数据对象只对一个事务可见

流程:
在这里插入图片描述
实现:

1.定义锁:一个数据节点可以被定义为一个锁,如/exclusive_lock/lock

2.获取锁:
在这里插入图片描述
3.释放锁:
当前拥有锁的客户端机器发送宕机或正常执行完业务逻辑时,会释放锁,即临时节点被删除;
zookeeper则会通知所有在/exclusive_lock节点上注册了子节点变更Watcher监听的客户端;
这些客户端收到通知后,再次重新发起分布式锁的获取

  • 共享锁

含义:数据对象对所有事务可见
在这里插入图片描述
流程:
在这里插入图片描述
实现:

1.定义锁:同样通过数据节点来表示一个锁,类似于”/shared_lock/[Hostname]-请求类型-序号“的临时顺序节点;可以知道,序号小的说明越先定义,能越早得到锁

2.获取锁:
在这里插入图片描述
3.判断读写顺序:
在这里插入图片描述
4.释放锁:跟排他锁一致

羊群效应:集群规模大时出现几率随着变大的问题
可能出现大量的”Watch通知“和”子节点列表获取“两个操作重复运行,且大多数的运行结果都是判断出自己并非是序号最小的节点,从而继续等待下一次通知;
客户端无端地接收到过多的和自己并不相关的事件通知,浪费了很多网络资源;
更严重的是,如果同一时间内有多个节点对应的客户端完成事务或事务中断引起节点消失,zookeeper服务器就会在短时间内向其余客户端发送大量的事件通知

出现羊群效应的本质原因:没有找准客户端真正的关注点
分布式锁竞争过程的核心逻辑:判断自己是否是所有子节点中序号最小的(序号小的先获取)
改进:每个节点对应的客户端只需要关注比自己序号小的那个相关节点的变更情况就可以了,而不需要关注全局的子列表变更情况,即关注/shared_lock节点下序号比自己小的那个节点是否存在即可,其实现如下:
在这里插入图片描述
流程:
在这里插入图片描述

8 分布式队列

分为先进先出队列和等到队列元素集聚之后才统一安排执行的Barrier模型

  • 先进先出队列

类似一个全写的共享锁模型,所有客户端都会到/queue_fifo节点下创建一个临时顺序节点

流程:
在这里插入图片描述

  • Barrier:分布式屏障

Barrier特指分布式系统的一个协调条件,规定了一个队列的元素必须都集聚后才能统一进行安排,否则一直在等待;如MapReduce会用上

设计思想:对/queue_barrier节点进行操作
在这里插入图片描述
流程:
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值