ElasticSearch集群选举

上一篇分析了es集群选举前,每个节点通过ping()获取集群内其他节点的信息。这一次从整体分析下es集群选举master的流程。

在Node节点的start()方法中,通过discovery.startInitialJoin()方法开始加入集群,并参与选举。

    @Override
    public void startInitialJoin() {
        // start the join thread from a cluster state update. See {@link JoinThreadControl} for details.
        synchronized (stateMutex) {
            // do the join on a different thread, the caller of this method waits for 30s anyhow till it is discovered
            joinThreadControl.startNewThreadIfNotRunning();
        }
    }

加锁,并调用startNewThreadIfNotRunning(),为了保证执行加入集群的控制线程的唯一性。

        public void startNewThreadIfNotRunning() {
            assert Thread.holdsLock(stateMutex);
            if (joinThreadActive()) {
                return;
            }
            threadPool.generic().execute(new Runnable() {
                @Override
                public void run() {
                    Thread currentThread = Thread.currentThread();
                    if (!currentJoinThread.compareAndSet(null, currentThread)) {
                        return;
                    }
                    while (running.get() && joinThreadActive(currentThread)) {
                        try {
                            innerJoinCluster();
                            return;
                        } catch (Exception e) {
                            logger.error("unexpected error while joining cluster, trying again", e);
                            // Because we catch any exception here, we want to know in
                            // tests if an uncaught exception got to this point and the test infra uncaught exception
                            // leak detection can catch this. In practise no uncaught exception should leak
                            assert ExceptionsHelper.reThrowIfNotNull(e);
                        }
                    }
                    // cleaning the current thread from currentJoinThread is done by explicit calls.
                }
            });
        }

可以看到,执行该方法先确保该线程此时握有锁。

其次如果此时该线程执行已经开启,那么为了确保唯一性,此次无需生成,直接退出。

        public boolean joinThreadActive() {
            Thread currentThread = currentJoinThread.get();
            return running.get() && currentThread != null && currentThread.isAlive();
        }

否则生成新的线程来执行,并通过cas将本线程保存在currentJoinThread成员上,该成员为原子变量保证线程安全。

        private final AtomicBoolean running = new AtomicBoolean(false);
        private final AtomicReference<Thread> currentJoinThread = new AtomicReference<>();

然后该唯一线程在线程工作时,不断循环执行innerJoinCluster()函数,准备开始加入集群。

 

在innerJoinCluster()中,首先通过循环调用findMaster()直到找到当前节点认定的master为止。

        while (masterNode == null && joinThreadControl.joinThreadActive(currentThread)) {
            masterNode = findMaster();
        }

在findMaster中,一开始先调用pingAndWait发送ping去同集群的其他节点,并等待和收集其他节点的ping的回复,得到fullPingResponses。

List<ZenPing.PingResponse> fullPingResponses = pingAndWait(pingTimeout).toList();

在pingAndWait方法中,就和我们上一篇分析的连接起来了。

    private ZenPing.PingCollection pingAndWait(TimeValue timeout) {
        final CompletableFuture<ZenPing.PingCollection> response = new CompletableFuture<>();
        try {
            zenPing.ping(response::complete, timeout);
        } catch (Exception ex) {
            // logged later
            response.completeExceptionally(ex);
        }

        try {
            return response.get();
        } catch (InterruptedException e) {
            logger.trace("pingAndWait interrupted");
            return new ZenPing.PingCollection();
        } catch (ExecutionException e) {
            logger.warn("Ping execution failed", e);
            return new ZenPing.PingCollection();
        }
    }

我们可以看到,先构造一个CompletableFuture,然后调用ping向其他节点发送ping请求,然后等待response::complete的回调函数,(在ping接收到其他请求返回后的最后accept调用)。然后等待response的get方法返回结果。

这时候我们已经得到集群内其他节点关于选举的ping请求的回复的集合。

当前节点没有提出一个master,把当前节点的选举信息也加入到收到的请求的集合中,由于此时是刚加入还未提出master,于是master为null。

        assert fullPingResponses.stream().map(ZenPing.PingResponse::node)
            .filter(n -> n.equals(localNode)).findAny().isPresent() == false;

        fullPingResponses.add(new ZenPing.PingResponse(localNode, null, this.clusterState()));

当然此时并没有master节点被选出。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值