《Elasticsearch 源码解析与优化实战》第5章:选主流程

news2024/11/16 2:42:11

《Elasticsearch 源码解析与优化实战》第5章:选主流程 - 墨天轮

一、简介

Discovery 模块负责发现集群中的节点,以及选择主节点。ES 支持多种不同 Discovery 类型选择,内置的实现称为Zen Discovery
,其他的包括公有云平台亚马逊的EC2、谷歌的GCE等。

本章讨论内置的 Zen Discovery 实现。Zen Discovery 封装了节点发现(Ping)、选主等实现过程,现在我们先讨论选主流程,在后面的章节中整体性介绍Discovery模块。

二、设计思想

所有分布式系统都需要以某种方式处理一致性问题。一般情况下,可以将策略分为两组

  • 试图避免不一致

  • 及定义发生不一致之后如何协调它们。后者在适用场景下非常强大,但对数据模型有比较严格的限制。因此这里研究前者,以及如何应对网络故障。

三、为什么使用主从模式

除主从(Leader/Follower) 模式外,另一种选择是分布式哈希表(DHT),可以支持每小时数千个节点的离开和加入,其可以在不了解底层网络拓扑的异构网络中工作,查询响应时间大约为4到10跳(中转次数),例如,Cassandra 就使用这种方案。但是在相对稳定的对等网络中,主从模式会更好。

ES的典型场景中的另一个简化是集群中没有那么多节点。通常,节点的数量远远小于单个节点能够维护的连接数,并且网络环境不必经常处理节点的加入和离开。这就是为什么主从模式更合适ES。

四、选举算法

在主节点选举算法的选择上,基本原则是不重复造轮子。最好实现一个众所周知的算法,这样的好处是其中的优点和缺陷是已知的。ES的选举算法的选择上主要考虑下面两种。

4.1、Bully算法

Leader 选举的基本算法之一。它假定所有节点都有一个唯一的 ID,使用该ID对节点进行排序。任何时候的当前Leader都是参与集群的最高ID节点。 该算法的优点是易于实现。但是,当拥有最大ID的节点处于不稳定状态的场景下会有问题。例如,Master 负载过重而假死,集群拥有第二大ID的节点被选为新主,这时原来的Master恢复,再次被选为新主,然后又假死......

ES通过推迟选举,直到当前的Master 失效来解决上述问题,只要当前主节点不挂掉,就不重新选主。但是容易产生脑裂(双主),为此,再通过“法定得票人数过半”解决脑裂问题。

4.2、Paxos算法

Paxos非常强大,尤其在什么时机,以及如何进行选举方面的灵活性比简单的Bully算法有很大的优势,因为在现实生活中,存在比网络连接异常更多的故障模式。但Paxos实现起来非常复杂。

五、相关配置

与选主过程相关的重要配置有下列几个,并非全部配置。

discovery.zen.minimum_master_nodes
:最小主节点数,这是防止脑裂、防止数据丢失的极其重要的参数。这个参数的实际作用早已超越了其表面的含义。除了在选主时用于决定“多数”,还用于多处重要的判断,至少包含以下时机:

  • 触发选主: 进入选主的流程之前,参选的节点数需要达到法定人数。

  • 决定Master: 选出临时的Master之后,这个临时Master需要判断加入它的节点达到法定人数,才确认选主成功。

  • gateway选举元信息: 向有 Master 资格的节点发起请求,获取元数据,获取的响应数量必须达到法定人数,也就是参与元信息选举的节点数。

  • Master发布集群状态: 发布成功数量为多数。

为了避免脑裂,它的值应该是半数以上(quorum):(master_eligible_nodes 2)+1

例如:如果有3个具备Master资格的节点,则这个值至少应该设置为(3/2) + 1=2。

该参数可以动态设置:
PUT _cluster/settings
{
    "persistent" : {
        "discovery.zen.minimum master_nodes" : 2
    }
}

discovery.zen.ping.unicast.hosts
:**集群的种子节点列表,构建集群时本节点会尝试连接这个节点列表,那么列表中的主机会看到整个集群中都有哪些主机。**可以配置为部分或全部集群节点。可以像下面这样指定:

discovery.zen.ping.unicast.hosts:
    -192.168.1.10:9300
    -192.168.1.11
    -seeds.mydomain.com

默认使用9300端口,如果需要更改端口号,则可以在IP后手工指定端口。也可以设置一个域名,让该域名解析到多个IP地址,ES会尝试连接这个IP列表中的全部地址。

  • discovery.zen.ping.unicast.hosts.resolve_timeout: DNS
    解析超时时间,默认为5秒。

  • discovery.zen.join_timeout
    : 节点加入现有集群时的超时时间,默认为ping_ timeout的20倍。

  • discovery.zen.join_retry_attemptsjoin_timeout
    :超时之后的重试次数,默认为3次。

  • discovery.zen.join_retry_delayjoin_timeout
    :超时之后,重试前的延迟时间,默认为100毫秒。

  • discovery.zen.master_election.ignore_non_master_ pings
    :设置为true时,选主阶段将忽略来自不具备Master资格节点(node.master: false)的ping请求,默认为false。

  • discovery.zen.fd.ping_interval
    :故障检测间隔周期,默认为1秒。

  • discovery.zen.fd.ping_timeout
    :故障检测请求超时时间,默认为30秒。

  • discovery.zen.fd.ping_retries
    :故障检测超时后的重试次数,默认为3次。

六、流程分析

6.0、流程概述

6.0.1、ZenDiscovery的选主过程如下:

  • 每个节点计算最小的已知节点 ID,该节点为临时Master。向该节点发送领导投票

  • 如果一个节点收到足够多的票数,并且该节点也为自己投票,那么它将扮演领导者的角色,开始发布集群状态

  • 所有节点都会参与选举,并参与投票,但是,只有有资格成为Master的节点(node.maste为true)的投票才有效。

获得多少选票可以赢得选举胜利,就是所谓的法定人数。在ES中,法定大小是一个可配置的参数。配置项: discovery.zen.minimum master_ nodes
。为了避免脑裂,最小值应该是有Maste资格的节点数n/2+1

6.0.2、整体流程可以概括为:

  • 选举临时Master;

  • 投票-确认Master,如果本节点当选,则等待确立Master,如果其他节点当选,则尝试加入集群,然后启动节点失效探测器;

  • 失效节点探测;

具体如下图所示。

执行本流程的线程池: generic。

下面我们具体分析每个步骤的实现。

6.1、选举临时Master

选举过程的实现位于ZenDiscovery#findMaster
。该函数查找当前集群的活跃Master,或者从候选者中选择新的Master。如果选 主成功,则返回选定的Master,否则返回空。

为什么是临时Master?因为还需要等待下一个步骤,该节点的得票数足够时,才确立为真正的Master。

临时Master的选举过程如下:

  • "ping"所有节点,获取节点列表 fullPingResponses, ping结果不包含本节点,把本节点单独添加到fullPingResponses中。

  • 构建两个列表。

    • activeMasters列表: 存储集群当前活跃Master列表。

    • masterCandidates列表: 存储master候选者列表

activeMasters列表:存储集群当前活跃Master列表。 遍历第一步 获取的所有节点,将每个节点所认为的当前Master节点加入activeMasters 列表中(不包括本节点)。在遍历过程中,如果配置了discovery.zen.master_election.ignore_non_master_pings
为true ( 默认为false),而节点又不具备Master资格,则跳过该节点。

具体流程如下图所示。

在这里插入图片描述

这个过程是将集群当前已存在的Master加入activeMasters列表,正常情况下只有一个。如果集群已存在Master,则每个节点都记录了当前Master是哪个,考虑到异常情况下,可能各个节点看到的当前Master不同。在构建activeMasters列表过程中,如果节点不具备Master资格,则可以通过ignore_non_master_pings
选项忽略它认为的那个Master。

masterCandidates列表:存储master候选者列表。 遍历第一步获取列表,去掉不具备Maste资格的节点,添加到这个列表中。如果activeMasters为空,则从masterCandidates中选举,结果可能选举成功,也可能选举失败。如果不为空,则从activeMasters中选择最合适的作为Master。

整体流程如下图所示。

在这里插入图片描述

6.1.1、从masterCandidates
中选主

与选主的具体细节实现封装在 ElectMasterService 类中,例如,判断候选者是否足够,选择具体的节点作为Master等。

从 masterCandidates 中选主时,首先需要判断当前候选者人数是否达到法定人数,否则选主失败。

public boolean hasEnoughCandidates (Collection<MasterCandidate> candidates) {
    //候选者为空,返回失败
    if (candidates.isEmpty()) {
        returnfalse;
    }
    //默认值为-1, 确保单节点的集群可以正常选主
    if (minimumMasterNodes < 1) {
        returntrue;
    }
    return candidates .size () >= minimumMasterNodes;
}

当候选者人数达到法定人数后,从候选者中选一个出来做Master:

public MasterCandidate electMaster (Collection<MasterCandidate> candidates) {
    List<MasterCandidate> sortedCandidates = new ArrayList<> (candidates);
    //通过自定义的比较函数对候选者节点从小到大排序
    sortedCandidates.sort (MasterCandidate :: compare);
    //返回最新的作为Master
    return sortedCandidates.get(0);
}

可以看出这里只是将节点排序后选择最小的节点作为Master。但是排序时使用自定义的比较函数MasterCandidate::compare,早期的版本中只是对节点ID进行排序,现在会优先把集群状态版本号高的节点放在前面。

使用默认比较函数的情况下,sort结果为从小到大排序。参考Long类型的比较函数的实现:

public static int compare (1ong X,long y) {
    return(x<y)?-1:((x==y)?0:1);
}

自定义比较函数的实现:
public static int compare(MasterCandidate cl, MasterCandidate c2) {
    //先比较集群状态版本,注意此处c2在前,c1在后
    int ret = Long.compare(c2.clusterStateVersion, c1.clusterStateVersion);
    //如果版本号相同,则比较节点ID
    if(ret==0){
        ret = compareNodes (c1.getNode(),c2.getNode());
    }
    return ret;
}

节点比较函数compareNodes的实现: 对于排序效果来说

  • 如果传入的两个节点中,有一个节点具备Master资格,而另一个不具备,则把有Master资格的节点排在前面。

  • 如果都不具备Master资格,或者都具备Master资格,则比较节点ID。

但是,masterCandidates 列表中的节点都是具备Master资格的。compareNodes 比较函数的两个if判断是因为在别的函数调用中会存在节点列表中可能存在不具备Master资格节点的情况。因此此处只会比较节点ID。

private static int compareNodes (DiscoveryNode o1, DiscoveryNode o2) {
    //两个if处理两节点中一个具备Master资格而另一个不具备的情况
    if (o1.isMasterNode() && !o2.isMasterNode () ) {
        return -1;
    }
    if (!o1.isMasterNode() && o2.isMasterNode()) {
        return1;
    }
    //通过节点ID排序
    return o1.getId().compareTo(o2.getId());
}

从 activeMasters 列表中选择列表存储着集群当前存在活跃的Master,从这些已知的Master节点中选择一个作为选举结果。选择过程非常简单,取列表中的最小值,比较函数仍然通过compareNodes实现,activeMasters 列表中的节点理论情况下都是具备Master资格的。

public DiscoveryNode tieBreakActiveMasters (Collection<DiscoveryNode> activeMasters)
    return activeMasters.stream().min(ElectMasterService ::compareNodes).get();
}

6.1.2、投票与得票的实现

在ES中,发送投票就是发送加入集群(JoinRequest)请求。得票就是申请加入该节点的请求的数量。收集投票,进行统计的实现在ZenDiscovery#handleJoinRequest
方法中,收到的连接被存储到ElectionContext#joinRequestAccumulator
中。当节点检查收到的投票是否足够时,就是检查加入它的连接数是否足够,其中会去掉没有Master资格节点的投票。

public synchronized int getPendingMasterJoinsCount() {
    int pendingMasterJoins = 0;
    //遍历当前收到的join请求
    for (DiscoveryNode node : joinReques tAccumulator .keySet()) {
        //过滤不具备master资格的节点
        if (node. isMasterNode()) {
            pendingMasterJoins++;
        }
    }
    return pendingMasterJoins;
}

6.1.3、确立Master或加入集群

选举出的临时Master 有两种情况:该临时Master是本节点或非本节点。为此单独处理。现在准备向其发送投票。

6.1.3.1、如果临时Master是本节点:

  • 等待足够多的具备Master资格的节点加入本节点(投票达到法定人数),以完成选举。超时(默认为30秒,可配置)后还没有满足数量的join请求,则选举失败,需要进行新一轮选举。

  • 成功后发布新的clusterState。

6.1.3.2、如果其他节点被选为Master:

  • 不再接受其他节点的join请求。

  • 向Master发送加入请求,并等待回复。超时时间默认为1分钟(可配置),如果遇到异常,则默认重试了3次(可配置)。这个步骤在joinElectedMaster方法中实现。

  • 最终当选的Master会先发布集群状态,才确认客户的join请求,因此, joinElectedMaster返回代表收到了join请求的确认,并且已经收到了集群状态。本步骤检查收到的集群状态中的Master节点如果为空,或者当选的Master不是之前选择的节点,则重新选举。

6.2、节点失效检测

到此为止,选主流程已执行完毕,Master 身份已确认,非Master节点已加入集群。节点失效检测会监控节点是否离线,然后处理其中的异常。失效检测是选主流程之后不可或缺的步骤,不执行失效检测可能会产生脑裂(双主或多主)。在此我们需要启动两种失效探测器:

  • 在Master节点,   启动 NodesFaultDetection
    ,简称NodesFD。定期探测加入集群的节点是否活跃。

  • 在非Master节点,启动MasterFaultDetection
    ,简称MasterFD。定期探测Master节点是否活跃。

NodesFaultDetection和MasterFaultDetection都是通过定期(默认为1秒)发送的ping请求探测节点是否正常的,当失败达到一定次数(默认为3次),或者收到来自底层连接模块的节点离线通知时,开始处理节点离开事件。

6.2.1、NodesFaultDetection 事件处理

检查一下当前集群总节点数是否达到法定节点数(过半),如果不足,则会放弃Master身份,重新加入集群。 为什么要这么做?设想下面的场景,如下图所示。

在这里插入图片描述

假设有5台机器组成的集群产生网络分区,2台组成一组,另外3台组成一组,产生分区前,原Master为Node1。此时3台一组的节点会重新选举并成功选取Noded3作为Master,会不会产生双主? NodesFaultDetection
就是为了避免上述场景下产生双主。

对应事件处理主要实现如下:在ZenDiscovery#handleNodeFailure
中执行NodeRemoval-ClusterStateTaskExecutor#execute

public ClusterTasksResult<Task> execute (final ClusterState currentState, final List<Task> tasks) throws Exception {
    //判断剩余节 点是否达到法定人数
    if (electMasterService.hasEnoughMasterNodes (remainingNodesClusterState.nodes()) == false) {
        finalint masterNodes = electMas terService.countMasterNodes(remainingNodesClusterState.nodes());
        rejoin.accept(LoggerMessageFormat.format("not enough master nodes(has \[{}\], but needed \[{}\])", masterNodes, electMasterService.minimumMasterNodes()));
        return resultBuilder .build (currentState) ;
    } else {
        return resultBuilder.build (allocationService.deassociateDeadNodes(remainingNodesClusterState, true, describeTasks(tasks)));
    }
}

主节点在探测到节点离线的事件处理中,如果发现当前集群节点数量不足法定人数,则放弃Master身份,从而避免产生双主。

6.2.2、MasterFaultDetection事件处理


**探测Master离线的处理很简单,重新加入集群。**本质上就是该节点重新执行一遍选主的流程。对应事件处理主要实现如下: ZenDiscovery#handleMasterGone

private void handleMasterGone (final DiscoveryNode masterNode, final Throwable cause, final String reason) {
    synchronized(stateMutex) {
        if (localNodeMaster() == false && masterNode.equals (committedState.get().nodes ().getMasterNode())) {
            pendingStatesQueue.failAllStatesAndClear (new ElasticsearchException("master left\[\[)\]", reason));
            //重新加入集群
            rejoin ("master left (reason = " + reason + ")");
        }
    }
}

小结

选主流程在集群中启动,从无主状态到产生新主时执行,同时集群在正常运行过程中,Master探测到节点离开,非Master节点探测到Master离开时都会执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/805136.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++之普通函数指针/类成员函数指针/lambda回调函数总结(一百六十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

边写代码边学习之卷积神经网络CNN

1. 卷积神经网络CNN 卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;CNN&#xff09;是一种深度学习神经网络的架构&#xff0c;主要用于图像识别、图像分类和计算机视觉等任务。它是由多层神经元组成的神经网络&#xff0c;其中包含卷积层、池化层和全连接…

算法与数据结构-二分查找

文章目录 什么是二分查找二分查找的时间复杂度二分查找的代码实现简单实现&#xff1a;不重复有序数组查找目标值变体实现&#xff1a;查找第一个值等于给定值的元素变体实现&#xff1a;查找最后一个值等于给定值的元素变体实现&#xff1a;查找最后一个小于给定值的元素变体实…

【雕爷学编程】MicroPython动手做(10)——零基础学MaixPy之神经网络KPU2

KPU的基础架构 让我们回顾下经典神经网络的基础运算操作&#xff1a; 卷积&#xff08;Convolution&#xff09;:1x1卷积&#xff0c;3x3卷积&#xff0c;5x5及更高的卷积 批归一化&#xff08;Batch Normalization&#xff09; 激活&#xff08;Activate&#xff09; 池化&…

玩一玩编程式 AOP

[toc] 平时我们项目中涉及到 AOP&#xff0c;基本上就是声明式配置一下就行了&#xff0c;无论是基于 XML 的配置还是基于 Java 代码的配置&#xff0c;都是简单配置即可使用。声明式配置有一个好处就是对源代码的侵入小甚至是零侵入。不过今天松哥要和小伙伴们聊一聊编程式的 …

Chapter 9: Lists | Python for Everybody 讲义笔记_En

文章目录 Python for Everybody课程简介ListsA list is a sequenceLists are mutableTraversing a listList operationsList slicesList methodsDeleting elementsLists and functionsLists and stringsParsing linesObjects and valuesAliasingList argumentsDebuggingGlossar…

【Spring】Spring 下载及其 jar 包

根据 【动力节点】最新Spring框架教程&#xff0c;全网首套Spring6教程&#xff0c;跟老杜从零学spring入门到高级 以及老杜的原版笔记 https://www.yuque.com/docs/share/866abad4-7106-45e7-afcd-245a733b073f?# 《Spring6》 进行整理&#xff0c; 文档密码&#xff1a;mg9b…

数字签名与数字证书

数字签名与数字证书 数字签名数字证书数字证书的原理数字证书的特点 如何验证证书机构的公钥不是伪造的 数字签名 数字签名是非对称密钥加密技术与数字摘要技术的应用&#xff0c;数字签名就是用加密算法加密报文文本的摘要&#xff08;摘要通过hash函数得到&#xff09;而生成…

「回溯框架」

文章目录 0 回溯和动态规划&#xff08;dp&#xff09;的区别0.1 框架 1 刷题1.1 全排列1.1.1 题解1.1.2 Code1.1.3 结果 1.2 N皇后1.2.1 题解1.2.2 Code1.2.3 结果 0 回溯和动态规划&#xff08;dp&#xff09;的区别 动态规划的核心是穷举&#xff0c;那么回溯算法和dp有什么…

单机最快的队列Disruptor解析和使用

前言 介绍高性能队列Disruptor原理以及使用例子。 Disruptor是什么? Disruptor是外汇和加密货币交易所运营商 LMAX group 建立高性能的金融交易所的结果。用于解决生产者、消费者及其数据存储的设计问题的高性能队列实现。可以对标JDK中的ArrayBlockingQueue。是目前单机且…

IDC报告背后:大模型时代,重新理解AI公有云

大模型之于AI公有云的意义&#xff0c;在于大模型可以改变过去“手工作坊定制算法”的高成本模式&#xff0c;转向“工厂模式”&#xff0c;只需要微调和精调&#xff0c;就可以形成针对性的场景算法。 作者|葛覃 出品|产业家 一年前&#xff0c;依然有不少云计算从业者思…

基于智能状态和源代码插桩的 C 程序内存安全性动态分析

原文来自微信公众号“编程语言Lab”&#xff1a;基于智能状态和源代码插桩的 C 程序内存安全性动态分析 搜索关注“编程语言Lab”公众号&#xff08;HW-PLLab&#xff09;获取更多技术内容&#xff01; 欢迎加入 编程语言社区 SIG-程序分析 参与交流讨论&#xff08;加入方式&a…

警惕!通过谷歌和必应搜索广告传播的新型恶意活动

据观察&#xff0c;一种新的恶意广告活动利用谷歌搜索和必应的广告&#xff0c;以AnyDesk、Cisco AnyConnect VPN和WinSCP等IT工具的用户为目标&#xff0c;诱骗他们下载木马安装程序&#xff0c;目的是入侵企业网络&#xff0c;并可能在未来实施勒索软件攻击。 Sophos在周三的…

Python生成pyc以及pyd文件的方法

文章目录 0. 背景1. pyc文件的生成2. pyd文件的生成3. 两者的异同 0. 背景 当有些模块的代码需要一定的保密性&#xff0c;这个时候就需要考虑pyc和pyd文件了。今天就好好琢磨一下这两种文件的生成和使用方法。让自己的知识能够朝着商业化的方向再前进一步。 1. pyc文件的生成…

为企业发展赋能增效:中国智能交通协会来访闪马智能

7月26日&#xff0c;中国智能交通协会秘书长杨颖一行来访闪马智能&#xff0c;闪马智能助理总裁兼营销与方案中心总经理黄智宏、CMO王一佳、副总裁詹诚以及副总裁兼智慧城市创新院院长邵钦豪等出席了交流会。 上海电科智能系统股份有限公司、卡斯柯信号有限公司、上海澳星照明电…

告别胆怯,大步向前,迎接新挑战!

告别胆怯&#xff0c;大步向前&#xff0c;迎接新挑战&#xff01; “赤日炎炎似火烧&#xff0c;野田禾稻半枯焦。农夫心内如汤煮。公子王孙把扇摇。”读罢《水浒传》中的这一首七绝诗&#xff0c;受其感染&#xff0c;笔者也乘兴呤顺口溜四句抒怀&#xff1a;“烈日炎炎似火…

FlatBuffers 使用编译器

1、前言 可能刚接触的人会思考为啥要使用编译器&#xff1a; 一般跨平台、跨语言的都有一套固定的流程&#xff0c;大致可分为&#xff1a; 撰写IDL文件 -> 使用对应语言的编译器&#xff0c;编译成对应的语言 -> 序列化 ->持久化 -> 反序列化 这里就对应着这个…

Spring中IOC容器常用的接口和具体的实现类

在Spring框架没有出现之前&#xff0c;在Java语言中&#xff0c;程序员们创建对象一般都是通过关键字new来完成&#xff0c;那时流行一句话“万物即可new&#xff0c;包括女朋友”。但是这种创建对象的方式维护成本很高&#xff0c;而且对于类之间的相互关联关系很不友好。鉴于…

三言两语说透关于 MySQL2 和 MySQL 的区别

MySQL是最流行的开源关系型数据库管理系统,拥有大量的使用者和广泛的应用场景。而MySQL2是MySQL官方团队推出的新一代MySQL驱动&#xff0c;用于取代老版的MySQL模块&#xff0c;提供更好的性能和更丰富的功能。本文将介绍MySQL2相较于MySQL有哪些优势以及具体的技术区别。 My…

01 关于 ABAP RAP 模型

ABAP RAP 模型 概览 关于 RAP 专栏内容&#xff0c;是个人在 SAP 官方提供的课程学习后整理的文档。这些文档涉及部分对概念的理解。在这里&#xff0c;不做具体讲解&#xff0c;而是跟随后续实例开发教程&#xff0c;通过实际练习&#xff0c;让大家能够理解这些概念。 ABAP …