1 LeaderSelector
0 基于InterProcessMutex分布式锁进行抢主,抢到锁的即为Leader
1 当实例被选为leader之后,调用takeLeadership方法进行业务逻辑处理,处理完成即释放领导权。
2 autoRequeue()方法的调用确保此实例在释放领导权后还可能获得领导权。默认为false。
1.1 组成
构造函数
client zk客户端实例
leaderPath Leader选举根节点路径
executorService master选举使用的线程池
listener 节点成为Leader后的回调
public LeaderSelector(CuratorFramework client, String leaderPath, ThreadFactory threadFactory, Executor executor, LeaderSelectorListener listener)
{
this(client, leaderPath, new CloseableExecutorService(wrapExecutor(executor), true), listener);
}
/**
* @param client the client
* @param leaderPath the path for this leadership group
* @param executorService thread pool to use
* @param listener listener
*/
public LeaderSelector(CuratorFramework client, String leaderPath, ExecutorService executorService, LeaderSelectorListener listener)
{
this(client, leaderPath, new CloseableExecutorService(executorService), listener);
}
/**
* @param client the client
* @param leaderPath the path for this leadership group
* @param executorService thread pool to use
* @param listener listener
*/
public LeaderSelector(CuratorFramework client, String leaderPath, CloseableExecutorService executorService, LeaderSelectorListener listener)
{
Preconditions.checkNotNull(client, "client cannot be null");
PathUtils.validatePath(leaderPath);
Preconditions.checkNotNull(listener, "listener cannot be null");
this.client = client;
this.listener = new WrappedListener(this, listener);
hasLeadership = false;
this.executorService = executorService;
mutex = new InterProcessMutex(client, leaderPath)
{
@Override
protected byte[] getLockNodeBytes()
{
return (id.length() > 0) ? getIdBytes(id) : null;
}
};
}
1.2 源码
//循环选举
public void autoRequeue()
{
autoRequeue.set(true);
}
//start()
// -- requeue()
// -- internalRequeue()
private synchronized boolean internalRequeue()
{
//没有进入抢主并且已经调用过start()方法
if ( !isQueued && (state.get() == State.STARTED) )
{
isQueued = true;
Future<Void> task = executorService.submit(new Callable<Void>()
{
@Override
public Void call() throws Exception
{
try
{
//开始抢主
doWorkLoop();
}
finally
{
clearIsQueued();
//如果设置了释放权限自动抢主 则重新开始抢主
if ( autoRequeue.get() )
{
//递归调用
internalRequeue();
}
}
return null;
}
});
ourTask.set(task);
return true;
}
return false;
}
void doWork() throws Exception
{
hasLeadership = false;
try
{
//利用Curator的分布式锁InterProcessMutex抢锁
mutex.acquire();
hasLeadership = true;
try
{
//测试用 可以忽略
if ( debugLeadershipLatch != null )
{
debugLeadershipLatch.countDown();
}
if ( debugLeadershipWaitLatch != null )
{
debugLeadershipWaitLatch.await();
}
//抢锁成功 触发监听器takeLeadership()回掉方法
listener.takeLeadership(client);
}
catch ( InterruptedException e )
{
Thread.currentThread().interrupt();
throw e;
}
catch ( Throwable e )
{
ThreadUtils.checkInterrupted(e);
}
finally
{
clearIsQueued();
}
}
catch ( InterruptedException e )
{
Thread.currentThread().interrupt();
throw e;
}
finally
{
//触发完监听器方法后 释放leader权限 释放分布式锁
if ( hasLeadership )
{
hasLeadership = false;
try
{
mutex.release();
}
catch ( Exception e )
{
ThreadUtils.checkInterrupted(e);
log.error("The leader threw an exception", e);
// ignore errors - this is just a safety
}
}
}
}
2 LeaderLatch
0 需要手动调用close()方法来释放leader权限释放leader才会进行下一轮选举
1 添加回调实现成功选举和失败选举。
2.0 主要方法 :
//调用start方法开始抢主
void start()
//调用close方法释放leader权限
void close()
//await方法阻塞线程,尝试获取leader权限,但不一定成功,超时失败
boolean await(long, java.util.concurrent.TimeUnit)
//判断是否拥有leader权限
boolean hasLeadership()
2.1核心流程 :
1 zk客户端往同一路径下创建临时节点,创建后回调callBack
2 在回调事件中判断自身节点是否是节点编号最小的一个
3 如果是,则抢主成功,如果不是,设置对前一个节点(编号更小的)的删除事件的监听器,删除事件触发后重新进行抢主
3 区别
1 leaderlatch需要调用close方法才能释放主导权,并且不能重新获得。leaderselector当执行完takeleadership方法后自动释放主导权,并且可以设置autorequeue重新再获取领导权
2 实现方式不同leaderselector使用分布式锁InterProcessMutex实现
3 LeaderSelector相对LeaderLatch也更灵活,在执行完takerLeaderShip中的逻辑后会自动释放Leader权限,也能调用autoRequeue自动重新抢主
参考
https://blog.csdn.net/hosaos/article/details/88727817