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

《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);//返回最新的作为Masterreturn 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);//如果版本号相同,则比较节点IDif(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.mzph.cn/news/12080.shtml

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

相关文章

Ansible单yaml文件部署Zabbix5.0监控平台

文章目录 Ansible单yaml文件部署Zabbix5.0监控平台节点规划案例实施基础环境准备编写剧本文件ZabbixWeb界面(1)改中文(2)添加监控主机 Ansible单yaml文件部署Zabbix5.0监控平台 节点规划 IP主机名节点192.168.200.10ansibleAnsible节点192.168.200.20zabbix-serverZabbix-ser…

深度学习入门(一):神经网络基础

一、深度学习概念 1、定义 通过训练多层网络结构对位置数据进行分类或回归&#xff0c;深度学习解决特征工程问题。 2、深度学习应用 图像处理语言识别自然语言处理 在移动端不太好&#xff0c;计算量太大了&#xff0c;速度可能会慢 eg.医学应用、自动上色 3、例子 使用…

Effective Java 案例分享(八)

39、使用注解而不是通过命名规则分类 如果需要对定义class&#xff0c;property&#xff0c;或者method进行分类管理&#xff0c;推荐的做法是使用注解对其添加类别&#xff0c;而不是通过命名规则分类。这里以JUnit为例&#xff1a; 在JUnit 3中&#xff0c;如果要写测试的方…

linux环境安装mysql数据库

一&#xff1a;查看是否自带mariadb数据库 命令&#xff1a;rpm -qa | grep mariadb 如果自带数据库则卸载掉重新安装 命令&#xff1a;yum remove mariadb-connector-c-3.1.11-2.el8_3.x86_64 二&#xff1a;将压缩文件上传到/user/local/mysql文件夹 或者直接下载 命令&a…

基于ssm+mysql+html道路养护管理系统

基于ssmmysqlhtml道路养护管理系统 一、系统介绍二、功能展示1.道路信息管理2.损害类型信息管理3.损害类型信息管理4.评定等级信息管理5.日常巡查信息管理6.定期检查信息管理 四、获取源码 一、系统介绍 系统主要功能&#xff1a;道路信息管理、损害类型信息管理、评定等级信息…

【网络原理】 (1) (应用层 传输层 UDP协议 TCP协议 TCP协议段格式 TCP内部工作机制 确认应答 超时重传 连接管理)

文章目录 应用层传输层UDP协议TCP协议TCP协议段格式TCP内部工作机制确认应答超时重传 网络原理部分我们主要学习TCP/IP协议栈这里的关键协议(TCP 和 IP),按照四层分别介绍.(物理层,我们不涉及). 应用层 我们需要学会自定义一个应用层协议. 自定义协议的原因? 当前的软件(应用…

【JAVASE】顺序和选择结构

⭐ 作者&#xff1a;小胡_不糊涂 &#x1f331; 作者主页&#xff1a;小胡_不糊涂的个人主页 &#x1f4c0; 收录专栏&#xff1a;浅谈Java &#x1f496; 持续更文&#xff0c;关注博主少走弯路&#xff0c;谢谢大家支持 &#x1f496; 顺序和选择 1. 顺序结构2. 分支结构2.1 …

Ubuntu18.04 下配置Clion

配置Clion 安装gcc、g、make Ubuntu中用到的编译工具是gcc©&#xff0c;g&#xff08;C&#xff09;&#xff0c;make(连接)。因此只需安装对应的工具包即可。Ubuntu下使用命令安装这些包&#xff1a; &#xff08;1&#xff09;安装gcc sudo apt install gcc&am…

解决Cannot resolve plugin org.apache.maven.plugins:xxxxxxxx

解决Cannot resolve plugin org.apache.maven.plugins:xxxxxxxx 方法一、检查配置设置 下图中三个方框圈出来的地方设置为自己的下载的maven地址&#xff0c;配置文件地址&#xff0c;仓库地址。刷新maven。 我个人试过没用&#xff0c;不过网上有的朋友用这个方法解决了。 …

Day 69-70:矩阵分解

代码&#xff1a; package dl;import java.io.*; import java.util.Random;/** Matrix factorization for recommender systems.*/public class MatrixFactorization {/*** Used to generate random numbers.*/Random rand new Random();/*** Number of users.*/int numUsers…

FANUC机器人实现2个RO输出信号互锁关联(互补)的具体方法

FANUC机器人实现2个RO输出信号互锁关联(互补)的具体方法 一般情况下,为了方便用户控制工装夹具上的电磁阀等控制工具,FANUC机器人出厂时给我们提供了8个RO输出信号,如下图所示,这8个RO信号可以各自单独使用。 那么,如果为了安全控制,需要将2个RO信号成对的进行安全互锁…

linux服务器安装redis

一、安装下载 下载安装参考文章 下载安装包地址&#xff1a;https://download.redis.io/releases/ 亲测有效&#xff0c;但是启动的步骤有一些问题 安装完成&#xff01;&#xff01;&#xff01; 二、启动 有三种启动方式 默认启动指定配置启动开机自启 说明&#xff1a…

下载JMeter的历史版本——个人推荐5.2.1版本

官网地址&#xff1a;https://archive.apache.org/dist/jmeter/binaries/

JVM-提问纯享版

一、内存区域 介绍下 Java 内存区域&#xff08;运行时数据区&#xff09;内存分配方式内存分配并发问题对象的访问定位的两种方式&#xff08;句柄和直接指针两种方式&#xff09; 二、垃圾回收 如何判断对象是否死亡&#xff08;两种方法&#xff09;。简单的介绍一下强引…

POI 导出 树形结构

参考文章&#xff1a;(327条消息) Excel树状数据绘制导出_excel导出树形结构_Deja-vu xxl的博客-CSDN博客https://blog.csdn.net/weixin_45873182/article/details/120132409?spm1001.2014.3001.5502 Overridepublic void exportPlus(String yearMonth, HttpServletRequest re…

【C语言】从零开始学习数组

&#x1f341; 博客主页:江池俊的博客 &#x1f4ab;收录专栏&#xff1a;C语言——探索高效编程的基石 &#x1f4bb; 其他专栏&#xff1a;数据结构探索 &#x1f4a1;代码仓库&#xff1a;江池俊的代码仓库 &#x1f3aa; 社区&#xff1a;C/C之家社区 &#x1f341; 如果觉…

【C++ 进阶】学习导论:C/C++ 进阶学习路线、大纲与目标

目录 一、C 学习路线 二、C 课程大纲与学习目标 &#xff08;1&#xff09;第一阶段&#xff1a;C 语言基础 &#xff08;2&#xff09;第二阶段&#xff1a;C 高级编程 &#xff08;3&#xff09;第三阶段&#xff1a;C 核心编程与桌面应用开发 &#xff08;4&#xf…

网络安全领域关键信息泄露事件引发关注

近日&#xff0c;一家知名网络安全公司发布了一份报告揭露了一起重大信息泄露事件。据称&#xff0c;该事件涉及大量敏感用户数据的泄露引发了全球网络安全领域的广泛关注。 根据报道&#xff0c;该事件发生在全球范围内涉及多个国家和组织。专家指出&#xff0c;此次泄露事件…

深入学习 Redis - 深挖经典数据类型之 zset

目录 前言 一、zset 类型 1.1、操作命令 zadd / zrange&#xff08;添加 / 查询&#xff09; zcard&#xff08;个数&#xff09; zcount&#xff08;区间元素个数&#xff09; zrevrange&#xff08;逆序展示&#xff09; zrangebyscore&#xff08;按分数找元素&#…

【UE5 多人联机教程】06-显示玩家名称

效果 可以看到玩家输入各自的名称&#xff0c;会显示到自己控制的角色头上。但是目前有一个BUG就是&#xff0c;当客户端加入游戏时会多创建一个服务端的角色。 步骤 1. 打开“BP_ThirdPersonCharacter”&#xff0c;添加一个控件组件&#xff0c;用于显示玩家名称 作为网格体…