Master 选举不只是「选出一个」,还要让 master 能管理 worker:发现 worker 加入和死亡、重新分配任务、自己挂了之后备用 master 能无缝接手。这篇用 kazoo 搭一个最小的 master/worker,杀 worker、杀 master,数任务有没有丢、有没有重复。
/workers = ['W1', 'W2', 'W3'] | /master-election 子节点数 = 2 1.2 秒后:已完成 72 / 300
所有状态都在 ZooKeeper 里:/workers(worker 的临时节点)、/tasks(待分配)、/assign/<worker>(已分配未完成)、/done(已完成)。选举复用上一篇的 Election。把任务从 /tasks 移到 /assign 用 multi,保证任务不会同时在两处或两处都没有。
master 日志里的事件: ['+3.6s WORKER_LOST W2 requeued=76']
最终 /done 里 300 / 300 个任务;重复执行 0 次;全部完成用时 7.1 s
各 worker 完成的任务数: {'W3': 135, 'W2': 23, 'W1': 142}worker 的临时节点随会话过期消失,master 比较前后两次的 worker 列表发现它死了。重复执行是靠 worker 完成时 create /done/t 判重(已存在则放弃)来避免的;但「任务做了一半副作用已发生」这种情况 ZooKeeper 管不了,要靠任务本身幂等。
MASTER_START 事件(相对 kill): ['M1@+-10.0s', 'M2@+3.3s'] 最终 /done = 500 / 500;重复执行 0 次;总用时 7.1 s
新 master 启动时从 ZooKeeper 读出状态继续分配。需要诚实说明我这个最小实现的一个漏洞:新 master 的 known(已知 worker 集合)从空开始,所以在 master 空窗期内死掉的 worker,它的 /assign 目录不会被回收。我没有去测这种情况(因此没有验证);真实实现应该在接管时扫描 /assign 下所有没有对应 /workers 节点的目录。