当前位置: 首页 > news >正文

技术解析:基于 ZooKeeper 实现高可用的主-从协调系统(通过例子深入理解Zookeeper如何进行协调分布式系统)

系列文章目录

第一章 ZooKeeper入门概述:Znode,Watcher,ZAB .


文章目录

  • 系列文章目录
  • 技术解析:基于 ZooKeeper 实现高可用的主-从协调系统
    • 一、 系统设计:角色与数据模型
    • 二、 主节点:选举、故障转移与状态监控
      • 2.1 主节点选举与高可用性
      • 2.2 监控从节点与任务
    • 三、 从节点:注册与任务接收
    • 四、 客户端:任务提交与结果获取
    • 五、 完整交互流程
    • 结论:ZooKeeper 的核心协调机制


技术解析:基于 ZooKeeper 实现高可用的主-从协调系统

在分布式计算中,实现可靠的进程协调是构建稳定系统的基础。关键问题包括如何选举唯一的领导者、如何动态管理集群成员以及如何进行任务分发。本文将详细阐述如何使用 Apache ZooKeeper 的基本功能,构建一个高可用的主-从(Master-Slave)模式的协调系统。(通过该例子理解Znode和Watcher 如何进行分布式协调)

我们将通过 ZooKeeper 的命令行工具 zkCli 来演示整个过程,以清晰地展示其底层工作原理。

一、 系统设计:角色与数据模型

该系统由三个功能角色和一套定义在 ZooKeeper 中的数据结构组成。

集群配置: 三台服务器分别为host1,host2,host3.
系统角色:

  1. 主节点 (Master): 系统的控制单元。其功能是监控从节点和任务的状态,并负责将任务分配给当前可用的从节点。
  2. 从节点 (Slave): 任务的执行单元。它向系统注册自身的存在,并监视等待主节点分配的任务。
  3. 客户端 (Client): 任务的提交单元。它负责创建新任务,并监视任务的最终完成状态。

ZooKeeper 数据模型 (Znode 结构):

系统的状态和协调逻辑通过 ZooKeeper 的 znode 树来表达。关键路径定义如下:

  • /master (临时节点): 用于实现主节点选举。成功创建此节点的进程即为主节点。
  • /workers (持久节点): 用于从节点注册。其下的每个子节点代表一个处于活动状态的从节点。
  • /tasks (持久节点): 用于任务提交。客户端在此路径下创建子节点以发布新任务,形成一个任务序列。
  • /assign (持久节点): 用于任务分配。主节点在此路径下为每个从节点创建专用的子路径,用于存放分配给该从节点的任务信息。

二、 主节点:选举、故障转移与状态监控

主节点的功能实现依赖于 ZooKeeper 提供的原子操作和事件通知机制。

2.1 主节点选举与高可用性

核心机制:利用临时节点的唯一性及其与客户端会话的生命周期绑定。

  1. 创建尝试 (Creation Attempt):
    所有候选主节点的进程都尝试在 /master 路径下创建一个临时节点 (-e)

     # host1 尝试创建 /master 节点
    [zk: host1:2181(CONNECTED) 1] create -e /master "master1.example"
    Created /master

    由于 Znode 创建是原子操作,只有一个进程能成功。该进程成为活动主节点 (Active Master)。节点的数据字段可用于存储其网络地址等元信息。

  2. 处理创建失败 (Handling Creation Failure):
    其他进程在尝试创建时,将收到节点已存在的错误。

     # 进程host2 尝试创建,操作失败
    [zk: host2:2181(CONNECTED) 0] create -e /master "master2.example"
    Node already exists: /master
    

    收到此错误的进程将进入备份主节点 (Backup Master) 状态。

  3. 故障转移机制 (Failover Mechanism):
    备份主节点必须能够检测到活动主节点的故障并接管其角色。

    核心机制:在 /master 节点上设置监视点 (Watch)。

    # 进程host2 (备份主节点) 对 /master 节点设置监视
    [zk: host2:2181(CONNECTED) 1] ls /master true
    []
    

    ls /master true 命令在 /master 节点上注册了一个一次性的监视点。若活动主节点的进程失效或网络连接中断,其 ZooKeeper 会话将超时。由于 /master 是一个临时节点,它会随着会อ会话的终止而被自动删除
    我们在当前主节点模拟该过程,在host1上删除/master
    此删除操作会触发一个 NodeDeleted 事件,该事件会通知所有在该节点上注册了监视点的备份主节点(host2)。

#host1[zk: host1:2181(CONNECTED) 2] delete /master
#host2[zk: host2:2181(CONNECTED) 2]WATCHER::WatchedEvent state:SyncConnected type:NodeDeleted path:/master

收到通知后,所有备份主节点host2将重新执行步骤1的创建尝试,从而使其中一个成为新的活动主节点。此流程确保了主节点角色的高可用性。此时host2成为了主节点。

[zk: host2:2181(CONNECTED) 2] create -e /master "master2.example"
Created /master

2.2 监控从节点与任务

活动主节点需要监控系统的动态变化,即从节点的可用性和新任务的提交情况。
创建workers 。tasks,和assign

[zk: host2:2181(CONNECTED) 3] create /workers ""
Created /workers
[zk: host2:2181(CONNECTED) 4] create /assign ""
Created /assign
[zk: host2:2181(CONNECTED) 5] create /tasks ""
Created /tasks
[zk: host2:2181(CONNECTED) 6] ls /
[zookeeper, workers, tasks, master, assign]

核心机制:活动住节点监视子节点列表的变化 (NodeChildrenChanged)。

# 监视 /workers 路径的子节点变化(是否有从节点注册)
[zk: host2:2181(CONNECTED) 7] ls /workers true
[]# 监视 /tasks 路径的子节点变化(是否有任务待处理)
[zk: host2:2181(CONNECTED) 8] ls /tasks true
[]

通过在 ls 命令中使用 true 参数,主节点可以监视 /workers/tasks 两个路径。

  • /workers 下有子节点被创建或删除时,表明有从节点上线或下线,主节点会收到通知。
  • /tasks 下有子节点被创建时,表明有新任务提交,主节点同样会收到通知。

三、 从节点:注册与任务接收

从节点负责执行具体任务,它需要向系统声明其可用性,并准备接收任务。

  1. 注册 (Registration):
    从节点启动后,在 /workers 路径下创建一个代表自身的临时节点
    (host1 ,host 3注册从节点)
    [zk: host1:2181(CONNECTED) 1]  create -e /workers/worker1.example.com "worker1.example.com:2224"
    Created /workers/worker1.example.com
    

主节点host2发现workers有了变化(NodeChildrenChanged ),知道有了从节点上线。由于Watcher的一次性,host2为了可以继续监控,需要重新申请一个workers的监视Watcher

	[zk: host2:2181(CONNECTED) 9]WATCHER::WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/workers[zk: host2:2181(CONNECTED) 9] ls /workers true[worker1.example.com]

Note: 使用临时节点是一种有效的存活性检测机制。只要从节点进程正常,该节点就存在。进程异常终止将导致节点自动删除,主节点可通过监视点立即感知到该从节点的离线。

  1. 准备接收任务 (Preparing for Assignment):
    每个从节点(例如 host1)在启动时,都会在 /assign 路径下创建一个以自身标识符(如 orker1.example.com)命名的持久性 Znode,例如 /assign/host1。这个 Znode 作为该从节点的专属任务队列。随后,从节点会在此 Znode 上设置一个子节点变化(NodeChildrenChanged)的监视点。当主节点向该路径下添加子节点(即分配新任务)时,该从节点会收到通知并处理任务。使用持久节点确保了即使在从节点短暂离线期间,分配给它的任务信息也不会丢失。

    # worker1 创建其任务分配路径
    [zk: host1:2181(CONNECTED) 2] create /assign/worker1.example.com ""
    Created /assign/worker1.example.com
    # 监视该路径,等待任务分配
    [zk: host1:2181(CONNECTED) 3] ls /assign/worker1.example.com true
    []
    

四、 客户端:任务提交与结果获取

客户端是任务的初始输入源。

  1. 任务提交
    客户端(用host3模拟)通过在 /tasks 路径下创建一个顺序节点 (-s) 来提交一个新任务。

    # 客户端提交一个任务,其数据为 "cmd"
    [zk: host3:2181(CONNECTED) 6] create -s /tasks/task- "cmd"
    Created /tasks/task-0000000001
    #客户端监控任务是否完成
    [zk: host3:2181(CONNECTED) 7] ls /tasks/task-0000000001 true
    []

    使用顺序节点,ZooKeeper 会自动为节点名附加一个单调递增的数字后缀。这提供了一个简单且可靠的分布式序列生成功能,可用于任务排序。

  2. 活动主节点分配任务处理

#主节点收到任务通知
[zk: host2:2181(CONNECTED) 5]
WATCHER::WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/tasks
#主节点将任务分配给host1
[zk: host2:2181(CONNECTED) 5] create -e /assign/worker1.example.com/task-0000000001 ""
Created /assign/worker1.example.com/task-0000000001
#主节点监控任务是否完成
[zk: host2:2181(CONNECTED) 6] ls /tasks/task-0000000001 true
[]
  1. host2处理任务,处理结束,告诉主节点和客户端结束:
#host1收到任务分配通知
[zk: host1:2181(CONNECTED) 4]
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/assign/worker1.example.com
#host1完成任务,将任务完成通知写入
[zk: host1:2181(CONNECTED) 4] create /tasks/task-0000000001/status "done"
Created /tasks/task-0000000001/status
#客户端和主节点检测任务状态,如果不是done,则表示任务执行出现错误,可以尝试从新提交等。
[zk: host3:2181(CONNECTED) 5]  get /tasks/task-0000000001/status
done
cZxid = 0x100000037
ctime = Sun Sep 07 02:00:09 PDT 2025
mZxid = 0x100000037
mtime = Sun Sep 07 02:00:09 PDT 2025
pZxid = 0x100000037
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
  1. 主节点和客户端接收通知
    # 客户端监视任务节点 /tasks/task-0000000001[zk: host3:2181(CONNECTED) 8]WATCHER::WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/tasks/task-0000000001# 主节点监视任务节点 /tasks/task-0000000001[zk: host2:2181(CONNECTED) 7]WATCHER::WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/tasks/task-0000000001

五、 完整交互流程

以下是一个任务从创建到完成的完整事件序列:

  1. 初始化: 主节点 master1 选举成功,开始监视 /workers/tasks
  2. 从节点注册: 从节点 worker1 启动,创建 /workers/worker1.example.com。主节点收到通知,更新其可用从节点列表。
  3. 客户端提交任务: 客户端创建顺序节点 /tasks/task-0000000001
  4. 主节点分配任务:
    • 主节点收到 /tasks 的变化通知,检测到新任务 task-0000000001
    • 主节点查询 /workers,选择可用的 worker1
    • 主节点在 worker1 的分配路径下创建任务指派节点:
      create /assign/worker1.example.com/task-0000000000 ""
  5. 从节点执行任务:
    • worker1 收到其分配路径 /assign/worker1.example.com 的子节点变化通知。
    • 它读取到新分配的任务 task-0000000001,并开始处理。
  6. 反馈结果:
    • worker1 完成任务后,在原始任务节点下创建状态子节点:
      create /tasks/task-0000000000/status "done"
  7. 客户端获取结果:
    • 客户端对 /tasks/task-0000000000 的监视被触发。
    • 客户端和主节点通过 get /tasks/task-0000000000/status 读取到结果 "done",任务流程结束。

结论:ZooKeeper 的核心协调机制

此示例展示了如何组合使用 ZooKeeper 的几个核心功能来实现一个复杂的分布式协调模式:

  • 临时节点 (Ephemeral Nodes): 用于实现成员存活性检测和资源独占锁定。
  • 顺序节点 (Sequential Nodes): 用于实现分布式队列或唯一 ID 生成。
  • 监视点 (Watches): 提供了一个高效的、一次性的事件通知机制,是构建事件驱动系统的基础。

通过这些原语,不同角色的进程无需直接网络通信,而是通过改变和监视 ZooKeeper 中的共享状态来协同工作。这种方式降低了系统各组件间的耦合度,并利用 ZooKeeper 的一致性保证来确保整个系统的正确性。


文章转载自:

http://qV1AQ3Xk.grxbw.cn
http://mxe48Z3k.grxbw.cn
http://RDuPiNxo.grxbw.cn
http://6rUH2xGi.grxbw.cn
http://BKFv9Q5R.grxbw.cn
http://sWfdsczJ.grxbw.cn
http://imIaDipx.grxbw.cn
http://jjoUzfiI.grxbw.cn
http://WHEz5d8k.grxbw.cn
http://weuvRqT9.grxbw.cn
http://gV0gGSTo.grxbw.cn
http://7vc0RiLY.grxbw.cn
http://dOyupO7q.grxbw.cn
http://fydHsWY9.grxbw.cn
http://yFF40epa.grxbw.cn
http://AUJp61vJ.grxbw.cn
http://kEiJTIAs.grxbw.cn
http://3uU3ixRI.grxbw.cn
http://mji8EY7B.grxbw.cn
http://SSAHjqwH.grxbw.cn
http://StPwXLmi.grxbw.cn
http://VVU1A226.grxbw.cn
http://lt83J9SH.grxbw.cn
http://DO3eXZEZ.grxbw.cn
http://tA334ekW.grxbw.cn
http://6EjK0qVL.grxbw.cn
http://Sc56Xlkj.grxbw.cn
http://Uc9hY8mD.grxbw.cn
http://scPeSCJV.grxbw.cn
http://HYfgW7SU.grxbw.cn
http://www.dtcms.com/a/371634.html

相关文章:

  • 虚拟机安装Rocky Linux系统过程中有时会出现一直灰屏情况
  • CamX-Camera常用编译命令和adb指南
  • 文件操作详解
  • 独角数卡对接蓝鲸支付平台实现个人
  • [Android] SAI(APKS安装器)v4.5
  • MySQL 主从读写分离架构
  • 软件可靠性基本概念
  • 无人机自组网系统的抗干扰技术分析
  • 对比Java学习Go——基础理论篇
  • centos9安装sentinel
  • 小迪安全v2023学习笔记(七十九讲)—— 中间件安全IISApacheTomcatNginxCVE
  • 关键字 const
  • 性能优化——首屏优化
  • Linux网络设备驱动程序深度理解
  • Unity AssetBundle详解
  • 小白AIGC短视频生成的第一课之混元AI视频
  • 通义万相wan2.2视频模型的基础模型与安装应用详解
  • JavaEE 进阶第三期:开启前端入门之旅(三)
  • Linux:NTP服务
  • 【多模态学习】QA3:FFN的作用?Embedding生成方法的BERT和Word2Vec?非线性引入的作用?
  • Tomcat 日志文件名的命名规范
  • 基于单片机的可燃性气体泄漏智能报警系统
  • Ubuntu系统下Python连接国产KingbaseES数据库实现增删改查
  • 【linux kernel 常用数据结构和设计模式】【数据结构 2】【通过一个案例属性list、hlist、rbtree、xarray数据结构使用】
  • 论文阅读:DMD | Improved Distribution Matching Distillation for Fast Image Synthesis
  • 深入解析三色标记算法
  • Python struct模块 | 使用pack函数进行字节序打包
  • 二叉树的前中后序遍历(迭代法)
  • Camx-系统默认创建camxoverridesettings.txt
  • SQL面试题及详细答案150道(101-115) --- 数据操纵与定义篇