基于ZK实现高可用服务

文章目录

  • 1. 基于LeaderElectionService服务实现Leader选举
  • 2. 基于LeaderRetrievalService服务获取Leader节点

HighAvailabilityServices接口主要提供了获取不同组件的LeaderRetrievalService服务和LeaderElectionService服务。LeaderRetrievalService服务用于获取组件的当前Leader地址,内部通过LeaderRetrievalListener监听新切换的Leader地址;LeaderElectionService服务基于ZK从Leader Contender中选举出新Leader,同一时间有且只有一个有效的Leader节点,获取Leader Ship的Contender才能对外提供服务。

1. 基于LeaderElectionService服务实现Leader选举

实现了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服务主要有几个重要接口:

  • LeaderLatchListener:监听当前的LeaderContender是否为Leader,帮助LeaderLatch组件选举出Leader
  • NodeCacheListener:监听节点的变化(创建、更新、删除),将节点的数据缓存在本地
  • ConnectionStateListener:监听、处理和ZK之间的连接状态

2. 基于LeaderRetrievalService服务获取Leader节点

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;
    }
}

你可能感兴趣的:(Flink,flink)