[ZooKeeper之五] 使用 ZooKeeper 实现主-从
在 [ZooKeeper之一] ZooKeeper简介 中,介绍了主-从架构,再简单回顾下,首先主-从架构中要有一个主,Master 接受客户端提交任务,同时监测每个 worker 的状态,并将任务分配给 worker 执行,worker 负责执行 Master 分配的任务并返回执行结果,Master 收到执行结果时将其返回给客户端。
![](https://img.haomeiwen.com/i2865141/7e5630074251e816.png)
下面讨论下需求:
(1)主节点选举和故障转移
主-从模式,首先要有一个主节点,由多个备用节点选举产生,选出主节点后,没当选主节点的备用节点就会设置一个对主节点的监听器,当主节点发生故障时,所有备用节点都会收到通知,并重新选举出新的主节点。
(2)从节点的动态检测
从节点负责执行主节点分配的任务,为了让主节点能感知到从节点的存在,需要在 ZooKeeper 的某一指定路径下(比如/workers
)创建一个代表工作节点对应的 znode,当某个从节点发生故障时,该 znode 应该被自动删除,所以使用临时节点来创建对应的znode。
(3)客户端和任务
客户端向系统中提交任务,并等待系统返回执行结果。同样地,我们需要在 ZooKeeper 的某一指定路径下(比如/tasks
)创建znode,每个znode表示一个任务,为了防止系统故障导致提交的任务丢失,所以表示任务的 znode 应该用持久节点。
接下来,启动好 ZooKeeper 服务端和客户端工具,实现它!
一、主节点
ZooKeeper 通过多个节点进行同时尝试创建某个znode(比如 /lock
),可以实现一个简单的分布式锁,哪个节点进程成功创建了 /lock
,就说它抢到了锁。锁原语同样可用于确定主节点,假如创建的znode为 /master
,为了防止抢到锁之后主节点挂掉之后,无法重新竞争出新的主节点,需要将 /master
以临时节点的形式创建,从锁的角度看,是先释放锁资源才能让备用节点们去抢锁。
这里启动多个 zkcli 终端来表示多个不同抢锁的节点
![](https://img.haomeiwen.com/i2865141/95c58173e3a77f77.png)
当一个节点去抢锁竞争主节点时,会遇到两种情况:一种是成功抢到锁;另一种是抢锁失败,提示节点已经存在,这时候需要去设置对应的监听器,这样当锁被释放时,可以收到通知重新抢锁。下面分别用节点1、节点2来表示这两种情况:
![](https://img.haomeiwen.com/i2865141/6441977bbbf1c980.png)
现在关掉节点1的终端,模拟主节点故障的情况,等过了超时时间,可以看到节点2收到通知
![](https://img.haomeiwen.com/i2865141/8804a7cd2b517b10.png)
这时候备用节点有机会抢到锁,由于这里只有一个备用节点没人抢,所以成功转正
![](https://img.haomeiwen.com/i2865141/80ca14f1decbf8d4.png)
主节点需要先创建约定好的目录来放工作节点、任务以及任务分配,并且需要动态监控工作节点和任务的变化,所以还需要设置工作节点目录和任务目录的监听器
![](https://img.haomeiwen.com/i2865141/86cf057aea7b8d52.png)
二、从节点
首先需要在 /workers
目录下创建一个子节点,然后从节点需要在 /assign
下创建一个子节点来接收主节点分配的任务,由于子节点需要动态检测分配任务的变化,所以还需要对分配任务目录设置监听器。
![](https://img.haomeiwen.com/i2865141/49bafcb04f9e5612.png)
三、客户端与任务
客户端通过在 /tasks
下创建znode来表示一个任务
![](https://img.haomeiwen.com/i2865141/c5245955042d5afc.png)
这里使用
-s
表示这是一个顺序节点,这样避免创建节点名的重复,当任务被执行结束后,会在 /task/task-id
下创建一个子节点来存储结果。客户端需要知道任务什么时候执行结束,所以需要监听 /tasks/task-id
变化![](https://img.haomeiwen.com/i2865141/866578f079986d3d.png)
因为前面主节点设置了任务目录的监听器,所以当客户端提交一个任务时,主节点会收到通知
![](https://img.haomeiwen.com/i2865141/873d04907c2d7a7a.png)
接着主节点检查任务和工作节点列表,分配任务
![](https://img.haomeiwen.com/i2865141/e6d68cb7b89b20a1.png)
从节点收到通知,检查任务列表,执行完之后创建存储执行结果的状态节点
![](https://img.haomeiwen.com/i2865141/a11531673583f410.png)
最后客户端收到通知,获取任务执行结果
![](https://img.haomeiwen.com/i2865141/9c8ca440e7b760cb.png)