依赖原理
- 在ZK中添加基本节点,路径程序定义,节点类型为持久节点(PERSISTENT)。
- 对需要竞选leader的每个进程,在ZK中分别添加基本节点的子节点,类型为临时自编号节点(EPHEMERAL_SEQUENTIAL),并保存创建返回的实际节点路径。
- 通过delete方式删除本进程创建的子节点,可以作为退出leader状态的方式。
- 基本节点的子节点类型为临时自编号节点(EPHEMERAL_SEQUENTIAL),当进程与ZK连接中断后,ZK会自动将该节点删除,确保了断连之后其他进程对leader的选举。
- 由于ZK自编号产生的路径是递增的,因此可以通过判断基本节点的子节点中最小路径数字编号的节点是否是本进程新建的节点来判断是否获得leader地位。
原理图示
利用zk实现的分布式leader节点选举实现原理如下:
若干进程分别尝试竞选leader,情况如下:
- (1)8个进程分别在ZK基本节点下创建临时自编号节点,获取创建成功后的实际路径
- (2)在基本节点子节点列表中,判断本进程创建节点编号是否为最小
- (3)最小编号进程获得leader地位
leader程序异常退出或者服务器异常导致leader进程无法执行leader功能:
- (1)进程将ZK中对应的临时节点删除,此时基本节点下路径最小的子节点将获得leader地位
- (2)进程由于网络或其他原因与ZK断开了连接,ZK自动将其对应的临时节点删除
- (3)新出现的进程加入leader竞选,在ZK下创建临时节点,排队等待
方案一 :父节点监听方式
实现原理
程序流程图如下:
实现代码
1 | package xuyihao.zktest.server.zk.leader; |
测试
测试程序
1 | private void zkLeaderOneTestWithMultiThread() throws Exception { |
结果:
1 | [2017-11-30 17:05:02] 线程: [0] 是主节点 |
方案一优劣
优点
- 实现对父节点变动状态(主要是子节点列表变化)的监听
- 当子节点列表出现变化后,ZK通知监听的各个进程,各个进程查询子节点状态
- 对父节点进行监听,实现起来相对简单
劣势
- 每个进程都监听父节点状态,即父节点出现变动(主要是子节点列表变化)后,ZK服务器需要通知到所有注册监听的进程,网络消耗和资源浪费比较大
方案三 :子节点监听方式
实现原理
程序流程图如下:
实现代码
1 | import org.apache.zookeeper.*; |
测试
测试程序
1 | private void zkLeaderTwoTestWithMultiThread() throws Exception { |
结果:
1 | [2017-11-30 16:47:41] 线程: [0] 是主节点 |
方案二优劣
优点
- 实现对子节点变动状态(排序在本进程对应节点之前的一个节点)的监听
- 被监听子节点变动(删除)之后,ZK通知本进程执行相应操作,判断是否成为leader
- 相对于父节点监听方式,子节点监听方式在每一次锁释放(或者节点变动)时,ZK仅通知到一个进程的watcher,节省了大量的网络消耗和资源占用
劣势
- 实现方式与程序逻辑较父节点监听来说比较繁琐
总结比较
程序复杂度:
父节点监听方式 < 子节点监听方式网络资源消耗:
父节点监听方式 >> 子节点监听方式程序可靠性
父节点监听方式 < 子节点监听方式