分布式学习手记06 - ZooKeeper 典型应用场景

1 分钟读完

ZooKeeper 是一个典型的发布/订阅模式的分布式数据管理与协调框架,提供丰富的数据节点类型和 Watcher 事件通知机制。

典型应用场景

提供分布式应用中都会涉及的核心功能,如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等。

数据发布/订阅

使用 ZooKeeper 实现配置中心功能,应用将配置信息发布到 ZooKeeper 集群部分节点上,供订阅者动态获取,实现配置信息的集中式管理和动态更新。

全局配置信息特点:数据量小、会动态变化、各节点共享同一副本

  • 传统本地配置文件方式:

    一旦集群规模变大,且配置信息变更逐渐频繁后,变更成本极大提高;

  • 使用 ZooKeeper 集群保存配置信息:

    客户端初始化阶段从 ZooKeeper 配置节点拉取原始数据,并在该节点注册一个数据变更的 Watcher 监听。一旦数据发生变更,所有订阅的客户端都会获取到变更通知,客户端重新拉取最新的配置数据。

负载均衡

负载均衡是分布式系统必备功能,将数据和服务根据系统中各对等节点的负载情况合理分配,保证集群高可用性。

本质上,DNS 系统类似一个超大规模的分布式映射表,因此,比照配置中心的实现, ZooKeeper 可以充当动态的自动化 DNS 服务中心。

如下,每个应用可以创建属于自己的数据节点作为域名配置的根节点,保存域名解析信息

/DDNS
  |__app1
  |    |__server.app1.company1.com
  |__app2
  • 域名注册

    服务提供者将自身业务的 IP、端口、域名信息发送给 Register,Register 将配置信息写入相应的 ZooKeeper 域名节点;

  • 域名解析

    系统 Dispatcher 收到服务消费者的解析请求后,从 ZooKeeper 上指定域名节点读取相应信息,通过负载均衡策略选出一个返回给消费者;

  • 域名变更

    当域名解析信息产生变更, ZooKeeper 向有订阅 Watcher 的客户端发送变更通知,应用收到通知后,重新获取最新数据;

  • 域名探测

    即域名健康检测,服务提供者定期向 Scanner 汇报状态,若 Scanner 超时未收到报告,则判定该服务提供者失效,清理 ZooKeeper 中该域名节点信息。

命名服务

Name Service 是分布式系统基本的公共服务之一,被命名的实体包括:集群中机器、提供的服务地址或远程对象等,这些被统称为名字(Name)。

通过使用命名服务,客户端应用可以通过指定名字来获取资源的实体、服务地址和提供者的信息等。

ZooKeeper 通过一套分布式全局唯一 ID 的分配机制,使得分布式环境中,上层应用可以通过全局唯一的 Name 定位和使用目标资源。

/jobs
  |__type1
  |   |__job-00000001
  |   |__job-00000002
  |   |__...
  |__type2
      |__job-00000001
      |__job-00000003
      |__...
  1. 客户端根据业务类型,在指定分类下调用 ZooKeeper 的 create() 接口创建一个顺序节点 job-x;

  2. ZooKeeper 自动以后缀形式为子节点添加序号,如 job-00000003;

  3. 客户端收到返回值,拼接上 type 字段,如 type2-job-00000003,即为全局唯一 ID。

分布式协调/通知

分布式环境中,需要一个协调者(Coordinator)控制整个系统的运行流程,从而将分布式协调的职责从应用中剥离,减少模块间耦合性,提高系统可扩展性。

1- 系统协调

/tasks
  |__taskA
       |__lastCommit
       |__instances
       |     |__[HostnameX]-001
       |     |__[HostnameY]-002
       |__status
  • 任务注册

    1- 任务执行节点,先在集群任务列表(tasks)创建子节点(taskA);

  • 任务热备

    多个 instance 应对业务执行过程中可能出现的故障:

    2- 参与客户端都将自己的主机名写入 instances 数据节点;

    3- ZooKeeper 为其分配临时顺序节点序号;

    4- 根据 “小序号优先”策略 ,序号最小的子节点对应客户端进入 “RUNNING” 状态,其他客户端设为 “STANDBY” 状态;

  • 热备切换

    5- 状态标识后,”RUNNING” 客户端开始执行 taskA;

    6- 若 “RUNNING” 客户端故障,与 ZooKeeper 断连,其对应临时数据节点被清除,订阅了 “子节点列表变更” Watcher 的其余 “STANDBY” 进程再次根据 “小序号优先” 开始新一轮 “RUNNING” 选举;

  • 记录执行状态

    执行 taskA 的 “RUNNING” 进程将运行上下文存储在 “lastCommit” 数据节点,热备切换后,新的执行进程读取上下文信息,继续执行 taskA;

  • 控制台协调

    控制组件将 taskA 相关信息以配置文件形式写入任务目录,以便所有任务参与者都能共享;

2- 分布式系统间的通信方式

  • 心跳检测

    不同机器在 ZooKeeper 一个指定节点下创建自己的临时子节点,以此临时节点判定各机器是否存活

    互相之间不需直接关联,都从 ZooKeeper 节点上检测动态,减少耦合

  • 进度汇报

    临时节点既可以判断任务机器是否存活,还能存储任务进度信息

  • 系统调度

    节点上数据变更以事件形式推送给客户端,响应调度

集群管理

集群管理包括集群监控与操作控制,利用 ZooKeeper 两大特性(Watcher 监听和变更推送 & 会话失效临时节点自动清除)来实现

分布式日志收集系统

典型日志系统架构中,需要收集日志的源机器被分为多个组别,每组对应一个收集器。但运行过程中,源机器和收集器集群规模、配置、信息会发生变动。

如何快速、合理、动态地为每个收集器分配对应的日志源机器,其本质也是一种变更管理。

/logs
  |__collector
       |__host1
       |__host2
       |__ ...
       |__hostN
            |__status
  • 注册收集器

    在 ZooKeeper 上创建收集器的根节点 /logs/collector ,每个收集器启动时,在收集器节点下创建自己的节点 /logs/collector/hostN;

    注:host 节点需为持久节点 —— 虽然临时节点可以利用自动清除特性标记收集器的存活性,但会丢失自有源机器列表。因此使用持久节点,再通过 /logs/collector/host/status 子节点表征收集器状态

  • 任务分发

    系统根据 collector 节点下子节点个数,将日志源机器分组,对应写入每一个收集器节点。这样每个收集器都能从自己的 host 节点上获取日志源机器列表,开始日志收集工作;

  • 动态分配

    系统中 /logs/collector 节点会随集群状态变化自动变更,当新的收集器加入或有故障下线,系统重新任务分发:

Master 选举

业务系统中的 Master 具有对分布式系统状态变更的决定权

  1. 优先在 ZooKeeper 上创建节点的机器,成为 Master;

  2. 其余机器则创建此节点的变更 Watcher,监控 Master 存活状况,准备参与下轮选举

分布式锁

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

常规利用关系型数据库排他性实现的进程互斥,数据库就成了该分布式系统的性能瓶颈

1- 排他锁

当前仅有一个事务获得锁,当锁释放后,其他等待获取锁的事务能收到通知

/exclusive_lock
  |__lock
  • 定义锁

    ZooKeeper 上一个数据节点来表示一个锁 /exclusive_lock/lock

  • 获取锁

    所有客户端需要资源时都要试图创建临时节点 /exclusive_lock/lock ,但只有一个客户端能创建成功,即获取了锁;

    其他未获取锁的客户端创建 /exclusive_lock/lock 临时节点的 Watcher,监听其变更

  • 释放锁

    当获取锁的客户端资源释放完毕或宕机,临时节点 /exclusive_lock/lock 被删除,其他客户端收到 Watcher 变更通知,重新进入“获取锁”流程

2- 共享锁

/shared_lock
  |__host1-R-00001
  |__host2-W-00002
  |__...
  |__hostN-R-0000N
  • 定义锁

    临时节点上包含客户端及其请求类型和系统排序

  • 获取锁

    客户端创建自己的临时节点 /shared_lock/hostN-?-0000N

  • 操作类型及顺序

    1. 读请求:小于自己序号的都是读请求,则开始读取;若前面有写请求则等待 Watcher 变更通知

    2. 写请求: 若自己不是最小序号,则等待

  • 释放锁

    同上

  • 改进

    上述方法客户端获取全量节点变更,资源开销较大,当集群规模庞大时,可以仅订阅序号小于自己的最后一个节点即可

分布式队列

1- 先入先出 FIFO

/queue_fifo
  |__host1-00001
  |__host2-00002
  |__...
  |__hostN-0000N

类似全写操作的共享锁

  1. 获取 /queue_fifo 节点下所有子节点

  2. 确定自己节点序号顺序

  3. 若自己不是序号最小节点则等待,监听序号小于自己的最后一个节点

  4. 收到 Watcher 通知后,重复步骤 1

2- 分布式屏障 Barrier

系统等待全部元素聚集后,统一安排

创建一个节点作为聚集完成条件,所有客户端订阅该 Watcher

分类:

更新时间: