实现了LeaderContender接口的组件会成为LeaderShip的竞争者,主动竞争LeaderShip。而LeaderElectionService服务会从LeaderContender中选举出一个Leader,具备LeaderShip的组件才能对外提供服务。
以ResourceManager为例,ResourceManager服务启动时,也会在内部启动它的LeaderElectionService服务:
/**
* 启动LeaderElectionService服务
*/
@Override
public void start(LeaderContender contender) throws Exception {
// 检查LeaderContender是否为null,null就抛异常
Preconditions.checkNotNull(contender, "Contender must not be null.");
Preconditions.checkState(leaderContender == null, "Contender was already set.");
LOG.info("Starting ZooKeeperLeaderElectionService {}.", this);
// 同步代码块,锁住的是Object对象
synchronized (lock) {
// CuratorFramework是用来和ZooKeeper交互的客户端
// 向CuratorFramework中添加UnhandledErrorListener(用来收集ZooKeeperLeaderElectionService中的异常信息),
// 这个监听器不会处理异常,而是会把异常信息交给LeaderContender#handleError()回调方法处理
client.getUnhandledErrorListenable().addListener(this);
leaderContender = contender;
// LeaderLatch组件(通过LeaderLatchListener)实现了对Leader的选举:LeaderLatchListener会监听当前的LeaderContender是否为Leader
leaderLatch.addListener(this);
leaderLatch.start();
// NodeCacheListener用来监听自身节点的变化,如果节点被创建/更新/删除,NodeCache就会更新缓存,并触发事件给注册的Listener
cache.getListenable().addListener(this);
cache.start();
// 添加ConnectionStateListener:用来监听、处理和ZooKeeper之间的连接状态(CONNECTED/SUSPENDED/RECONNECTED/LOST),handleStateChange()方法会处理不同状态
client.getConnectionStateListenable().addListener(listener);
running = true;
}
}
LeaderElectionService服务主要有几个重要接口:
LeaderRetrievalService服务用于获取集群组件的当前Leader节点,内部通过LeaderRetrievalListener监听新切换的Leader地址。当LeaderRetrievalService服务启动时,需要绑定LeaderRetrievalListener(监听目标组件Leader的地址切换),一旦Leader发生变更,LeaderRetrievalListener#notifyLeaderAddress()就会通知监听者,从而能第一时间获取到有效的Leader地址。
例如TaskExecutor需要获取ResourceManager的Leader节点,在TaskExecutor的构造方法中首先获取到ResourceManager的LeaderRetrievalService服务:
// 通过高可用服务提供的“获取xxx组件的Leader服务”,获取ResourceManager组件中的Leader
this.resourceManagerLeaderRetriever = haServices.getResourceManagerLeaderRetriever();
/**
* HA服务中,要获取ResourceManager组件中的Leader,这得借助ZooKeeper完成
*/
@Override
public LeaderRetrievalService getResourceManagerLeaderRetriever() {
// 借助ZooKeeper创建LeaderRetrievalService
return ZooKeeperUtils.createLeaderRetrievalService(client, configuration, RESOURCE_MANAGER_LEADER_PATH);
}
public static ZooKeeperLeaderRetrievalService createLeaderRetrievalService(
final CuratorFramework client,
final Configuration configuration,
final String pathSuffix) {
String leaderPath = configuration.getString(
HighAvailabilityOptions.HA_ZOOKEEPER_LEADER_PATH) + pathSuffix;
// start方法会将 ZooKeeperLeaderRetrievalService服务启动起来
return new ZooKeeperLeaderRetrievalService(client, leaderPath);
}
在TaskExecutor启动自己的时候,会把这个LeaderRetrievalService服务也一并启动起来:
/**
* 启动TaskExecutor,本质上就是对TaskExecutor内部服务进行初始化
*/
private void startTaskExecutorServices() throws Exception {
try {
// start by connecting to the ResourceManager
// 启动ResourceManager组件的领导节点的监听服务,ResourceManagerLeaderListener可以监听ResourceManager的领导节点的变化情况。
// 一旦ResourceManager的领导节点变更,监听器就能知道,并能跟最新的Leader节点重新建立RPC连接。
// 通过LeaderRetrievalService服务,创建TaskExecutor与ResourceManager之间的RPC连接,此时会将TaskManager的资源信息汇报给ResourceManager
// 这个监听器在TaskExecutor启动时会初始化调用一次,以后只有当ResourceManager的Leader地址发生变更时才会被调用
resourceManagerLeaderRetriever.start(new ResourceManagerLeaderListener());
// tell the task slot table who's responsible for the task slot actions
// 启动TaskSlotTable服务,它是用来管理TaskManager内的Slot计算资源
taskSlotTable.start(new SlotActionsImpl(), getMainThreadExecutor());
// start the job leader service
// 启动JobLeaderService服务,它是TaskExecutor用来和JobManager进行RPC通信时,获取JobManager的Leader节点的
jobLeaderService.start(getAddress(), getRpcService(), haServices, new JobLeaderListenerImpl());
// FileCache是用来存储Task执行过程中,从PermanentBlobService拉取来的文件,并将其放到/tmp_/路径下。
// 如果Task处于“非注册”状态的时间超过5s,就将这个临时文件clear掉
fileCache = new FileCache(taskManagerConfiguration.getTmpDirectories(), blobCacheService.getPermanentBlobService());
} catch (Exception e) {
handleStartTaskExecutorServicesException(e);
}
}
由于这是TaskExecutor想要获取ResourceManager的Leader节点,因此LeaderRetrievalService服务绑定的监听器为ResourceManagerLeaderListener。
可以看出,LeaderElectionService服务选举Leader和LeaderRetrievalService获取Leader节点都是一个套路。
/**
* 启动LeaderRetrievalListener服务
*/
@Override
public void start(LeaderRetrievalListener listener) throws Exception {
// 检查传入的监听器LeaderRetrievalListener是否为null,为null就抛异常
Preconditions.checkNotNull(listener, "Listener must not be null.");
Preconditions.checkState(leaderListener == null, "ZooKeeperLeaderRetrievalService can " +
"only be started once.");
LOG.info("Starting ZooKeeperLeaderRetrievalService {}.", retrievalPath);
// 同步代码块,锁的依然是Object
synchronized (lock) {
// (监听ResourceManager的Leader的)ResourceManagerLeaderListener是LeaderRetrievalListener的实现子类
leaderListener = listener;
// UnhandledErrorListener负责捕获异常
client.getUnhandledErrorListenable().addListener(this);
// 向NodeCache添加NodeCacheListener,监听NodeCache中节点的变化。
// 当节点发生变更,即使选择新的Leader节点,并通过LeaderRetrievalListener#notifyLeaderAddress()通知监听方
cache.getListenable().addListener(this);
cache.start();
// ConnectionStateListener负责监听和ZooKeeper之间的连接状态
client.getConnectionStateListenable().addListener(connectionStateListener);
running = true;
}
}