常德市建设网站,wordpress模板淘宝客模板自适应,vps可以做多少网站,能免费做微信群推广的网站《Elasticsearch 源码解析与优化实战》第5章#xff1a;选主流程 - 墨天轮
一、简介
Discovery 模块负责发现集群中的节点#xff0c;以及选择主节点。ES 支持多种不同 Discovery 类型选择#xff0c;内置的实现称为Zen Discovery ,其他的包括公有云平台亚马逊的EC2、谷歌…《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) 12。该参数可以动态设置:
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/21 。
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 (CollectionMasterCandidate candidates) {//候选者为空返回失败if (candidates.isEmpty()) {returnfalse;}//默认值为-1 确保单节点的集群可以正常选主if (minimumMasterNodes 1) {returntrue;}return candidates .size () minimumMasterNodes;
}当候选者人数达到法定人数后从候选者中选一个出来做Master
public MasterCandidate electMaster (CollectionMasterCandidate candidates) {ListMasterCandidate sortedCandidates new ArrayList (candidates);//通过自定义的比较函数对候选者节点从小到大排序sortedCandidates.sort (MasterCandidate :: compare);//返回最新的作为Masterreturn sortedCandidates.get(0);
}可以看出这里只是将节点排序后选择最小的节点作为Master。但是排序时使用自定义的比较函数MasterCandidate::compare早期的版本中只是对节点ID进行排序现在会优先把集群状态版本号高的节点放在前面。
使用默认比较函数的情况下sort结果为从小到大排序。参考Long类型的比较函数的实现
public static int compare (1ong Xlong y) {return(xy)?-1:((xy)?0:1);
}自定义比较函数的实现:
public static int compare(MasterCandidate cl, MasterCandidate c2) {//先比较集群状态版本注意此处c2在前c1在后int ret Long.compare(c2.clusterStateVersion, c1.clusterStateVersion);//如果版本号相同则比较节点IDif(ret0){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 (CollectionDiscoveryNode 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 ClusterTasksResultTask execute (final ClusterState currentState, final ListTask 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离开时都会执行。