分布式系统中,对于一个复杂的任务,我们经常会选举一个Master节点,借助zk我们可以轻易的实现此项任务,在kafka以及spark的standalone模式下都是使用zk来实现Master选举,本文先从原理方面介绍为什么zk可以实现Master选举,然后利用curator的api进行Master选举实战。
zk进行Master选举原理
zookeeper的节点有两种类型: 持久节点和临时节点。临时节点有个非常重要的性质,如果注册这个节点的机器失去连接,那么这个节点会被zookeeper删除。选主过程就是利用这个特性,在服务器启动的时候,去zookeeper特定的一个目录下注册一个临时节点(这个节点作为master,谁注册了这个节点谁就是master),注册的时候,如果发现该节点已经存在,则说明已经有别的服务器注册了(也就是有别的服务器已经抢主成功),那么当前服务器只能放弃抢主,作为从机存在。同时,抢主失败的当前服务器需要订阅该临时节点的删除事件,以便该节点删除时(也就是注册该节点的服务器宕机了或者网络断了之类的)进行再次抢主操作。Maser选举的过程,其实就是简单的争抢在zookeeper注册临时节点的操作,谁注册了约定的临时节点,谁就是master。
Master选举实战
curator对原生api进行了封装,将节点创建,时间监听和自动选举过程进行了封装,我们只需要调用API即可实现Master选举。本文分别使用LeaderSelector和LeaderLatch两种方式,进行server模拟真实的运行场景。
LeaderSelector
LeaderSelector是利用Curator中InterProcessMutex分布式锁进行抢主,抢到锁的即为Leader。
-
LeaderSelector
org.apache.curator.framework.recipes.leader.LeaderSelector //开始抢主 void start() //在抢到leader权限并释放后,自动加入抢主队列,重新抢主 void autoRequeue()
-
LeaderSelectorListener是LeaderSelector客户端节点成为Leader后回调的一个监听器,在takeLeadership()回调方法中编写获得Leader权利后的业务处理逻辑。
`org.apache.curator.framework.recipes.leader.LeaderSelectorListener` //抢主成功后的回调 void takeLeadership()
-
LeaderSelectorListenerAdapter是实现了LeaderSelectorListener接口的一个抽象类,封装了客户端与zk服务器连接挂起或者断开时的处理逻辑(抛出抢主失败CancelLeadershipException),一般监听器推荐实现该类。
Server
我们模拟的server继承LeaderSelectorListenerAdapter
,该监听器会在获取到Master权利时候回调改监听器,执行takeLeadership
方法,在这里我们只打印改server的名字,当一个实例成为Master后,其他实例会进入等待,直到当前Master挂了或者退出后才会进行新的选举。
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.leader.LeaderSelector;
import org.apache.curator.framework.recipes.leader.LeaderSelectorListenerAdapter;
import java.io.Closeable;
public class Server extends LeaderSelectorListenerAdapter implements Closeable {
/** server name **/
private final String serverName;
/** listener */
private final LeaderSelector leaderSelector;
/** takeLeadership方法中设置线程阻塞多长时间,单位ms */
private final int SLEEP_MILLISECOND = 100000;
public Server(CuratorFramework client, String path, String serverName) {
this.serverName = serverName;
/** client, zk-path, listener */
leaderSelector = new LeaderSelector(client, path, this);
/** 允许多次参与选主**/
leaderSelector.autoRequeue();
}
public void start() {
leaderSelector.start();
System.out.println(getServerName() + "开始运行了!");
}
@Override
public void close() {
leaderSelector.close();
System.out.println(getServerName() + "释放资源了!");
}
@Override
public void takeLeadership(CuratorFramework client) {
try {
System.out.println(getServerName() + "是Master, 执行到takeLeadership()方法了!")