文章目录

  • 背景
    • 为什么使用主从模式?
    • 选举算法
    • 什么时候触发选主?
  • 选主过程
    • 选举临时Master
    • 投票与得票的实现
    • 确立Master或加入集群
    • 选举完成

elasticsearch中的Discovery模块负责发现集群中的节点,以及选择主节点。在Elasticsearch 7.0以前,内置的实现称为Zen Discovery。Zen Discovery封装了节点发送(ping)、选主等实现过程。

背景

为什么使用主从模式?

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

选举算法

Bully算法是Leader选举的基本算法之一。它假定所有节点都有一个唯一的ID,使用该ID对节点进行排序。任何时候的当前Leader都是参与集群的最高ID节点。该算法的优点是易于实现。

但是,当拥有最大ID的节点处于不稳定状态的场景下会有问题。例如,Master负载过重而假死,集群拥有第二大ID的节点被选为新主,这时原来的Master恢复,再次被选为新主,然后又假死…。ES通过推迟选举,直到当前的Master失效来解决上述问题,只要当前主节点不挂掉,就不重新选主。

但是容易产生脑裂(双主),为此,再通过“法定得票人数过半”解决脑裂问题。

7.X之后的ES,采用一种新的选主算法,实际上是Raft的实现,但并非严格按照Raft论文实现,而是做了一些调整。

什么时候触发选主?

主要在以下三个场景会触发选主:

  1. 集群启动初始化;
  2. 集群的Master崩溃的时候;
  3. 任何一个节点发现当前集群中的Master节点没有得到n/2 + 1节点认可的时候。

不执行失效检测可能会产生脑裂(双主或多主),因此需要启动两种失效探测器:

  • 在Master节点,启动NodesFaultDetection,简称NodesFD。定期探测加入集群的节点是否活跃。检查一下当前集群总节点数是否达到法定节点数(过半),如果不足,则会放弃Master身份,重新加入集群。
  • 在非Master节点启动MasterFaultDetection,简称MasterFD。定期探测Master节点是否活跃。探测Master离线的处理很简单,重新加入集群。本质上就是该节点重新执行一遍选主的流程。

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

选主过程

ZenDiscovery选主的整体流程可以概括为:选举临时Master,如果本节点当选,则等待确立Master,如果其他节点当选,则尝试加入集群,最后选举完成。流程图如下:

选举临时Master

通过ZenDiscovery#findMaster确定临时master。

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

private DiscoveryNode findMaster() {// 1. PING 所有节点,获取各节点保存的集群信息List<ZenPing.PingResponse> fullPingResponses = pingAndWait(pingTimeout).toList();if (fullPingResponses == null) {logger.trace("No full ping responses");return null;}// 2. 由于上面是获取的其他节点的信息,这里需要将本节点加上final DiscoveryNode localNode = transportService.getLocalNode();// add our selvesassert fullPingResponses.stream().map(ZenPing.PingResponse::node).filter(n -> n.equals(localNode)).findAny().isPresent() == false;fullPingResponses.add(new ZenPing.PingResponse(localNode, null, this.clusterState()));// 3. 若设置了 master_election_ignore_non_masters 则去掉没有 master 资格(node.master: false)的节点final List<ZenPing.PingResponse> pingResponses = filterPingResponses(fullPingResponses, masterElectionIgnoreNonMasters, logger);// 4. 将各节点认为的 master 加入 activeMasters 列表List<DiscoveryNode> activeMasters = new ArrayList<>();for (ZenPing.PingResponse pingResponse : pingResponses) {// 避免未经其他节点检查就将本节点选为 masterif (pingResponse.master() != null && !localNode.equals(pingResponse.master())) {activeMasters.add(pingResponse.master());}}// 5. 将 PING 到的具有 master 资格的节点加入 masterCandidates 列表作为候选节点List<ElectMasterService.MasterCandidate> masterCandidates = new ArrayList<>();for (ZenPing.PingResponse pingResponse : pingResponses) {if (pingResponse.node().isMasterNode()) {masterCandidates.add(new ElectMasterService.MasterCandidate(pingResponse.node(), pingResponse.getClusterStateVersion()));}}if (activeMasters.isEmpty()) {// 6. 没有活跃的 masterif (electMaster.hasEnoughCandidates(masterCandidates)) {// 7. 拥有足够的候选节点,则进行选举final ElectMasterService.MasterCandidate winner = electMaster.electMaster(masterCandidates);return winner.getNode();} else {// 8. 无法选举,无法得到 master,返回 nullreturn null;}} else {// 9. 有活跃的 master,从 activeMasters 中选择assert !activeMasters.contains(localNode) :"local node should never be elected as master when other nodes indicate an active master";// lets tie break between discovered nodesreturn electMaster.tieBreakActiveMasters(activeMasters);}
}

临时Master的选举过程如下:

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

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

  3. 如果activeMaster为空,则从masterCandidates中选举,结果可能选举成功,也可能选举失败。如果不为空,则从acitveMasters中选择最合适的作为Master。

  • 从masterCandidates中选主:与选主的具体细节实现封装在ElectMasterService类中。首先需要判断 当前候选者人数是否达到法定人数,否则选主失败。接着从候选者中选一个出来做Master,优先把集群状态版本号高的节点放在前面。
  • 从activeMasters列表中选择:列表存储着集群当前存在活跃的Master,从中选择一个作为选举结果。上面第9步通过 ElectMasterService::tieBreakAcitveMasters使用ElectMasterService::compareNodes的规则从activeMasters中选择。

投票与得票的实现

在ES中,发送投票就是发送加入集群(JoinRequest)请求。

// ZenDiscovery#joinElectedMaster,加入一个新选出来的主节点
private boolean joinElectedMaster(DiscoveryNode masterNode) {while (true) {membership.sendJoinRequestBlocking(masterNode, transportService.getLocalNode(), joinTimeout);}
}

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

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

确立Master或加入集群

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

// ZenDiscovery#innerJoinCluster
// join线程的主函数。该函数被用来保证是加入集群或是当失败时创建一个新的join线程。
private void innerJoinCluster() {if (transportService.getLocalNode().equals(masterNode)) {// 选出的临时 master 是本节点,则等待被选举为真正的 masterfinal int requiredJoins = Math.max(0, electMaster.minimumMasterNodes() - 1); // we count as onenodeJoinController.waitToBeElectedAsMaster(requiredJoins, masterElectionWaitForJoinsTimeout,new NodeJoinController.ElectionCallback() {@Overridepublic void onElectedAsMaster(ClusterState state) {// 成功被选举为 mastersynchronized (stateMutex) {joinThreadControl.markThreadAsDone(currentThread);}}@Overridepublic void onFailure(Throwable t) {// 等待超时,重新开始选举流程synchronized (stateMutex) {joinThreadControl.markThreadAsDoneAndStartNew(currentThread);}}});} else {// 选出的临时 master 不是本节点,不再接收其他节点的 join 请求nodeJoinController.stopElectionContext(masterNode + " elected");// 向临时节点发送 join 请求(投票),被选举的临时 master 在确认成为 master 并发布新的集群状态后才会返回final boolean success = joinElectedMaster(masterNode);// 成功加入之前选择的临时 master 节点,则结束线程,否则重新选举synchronized (stateMutex) {if (success) {DiscoveryNode currentMasterNode = this.clusterState().getNodes().getMasterNode();if (currentMasterNode == null) {joinThreadControl.markThreadAsDoneAndStartNew(currentThread);} else if (currentMasterNode.equals(masterNode) == false) {// update cluster statejoinThreadControl.stopRunningThreadAndRejoin("master_switched_while_finalizing_join");}joinThreadControl.markThreadAsDone(currentThread);} else {// failed to join. Try again...joinThreadControl.markThreadAsDoneAndStartNew(currentThread);}}}
}

如果临时Master是本节点:

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

如果其他节点被选为Master:

  1. 不再接受其他节点的join请求。
  2. 向Master发送加入请求,并等待回复。超时时间默认为1分钟(可配置),如果遇到异常,则默认重试3次(可配置)。这个步骤在joinElectedMaster方法中实现。
  3. 最终当选的Master会先发布集群状态,才确认客户的join请求,因此,joinElectedMaster返回代表收到了join请求的确认,并且已经收到了集群状态。本步骤检查收到的集群状态中的Master节点如果为空,或者当选的Master不是之前选择的节点,则重新选举。

选举完成

NodeJoinController::checkPendingJoinsAndElectIfNeeded在节点获得足够的得票时使节点成为正式master,并发布新的集群状态

private synchronized void checkPendingJoinsAndElectIfNeeded() {// 计算节点得票数final int pendingMasterJoins = electionContext.getPendingMasterJoinsCount();if (electionContext.isEnoughPendingJoins(pendingMasterJoins) == false) {...} else {// 得票数足够,成为 masterelectionContext.closeAndBecomeMaster();}
}public synchronized int getPendingMasterJoinsCount() {int pendingMasterJoins = 0;// 统计节点得票数,只计算拥有 master 资格节点的投票for (DiscoveryNode node : joinRequestAccumulator.keySet()) {if (node.isMasterNode()) {pendingMasterJoins++;}}return pendingMasterJoins;
}

Elasticsearch中的Zen Discovery选主流程相关推荐

  1. 【Elasticsearch选主流程】

    Discovery模块负责发现集群中的节点,以及选择主节点.ES支持多种不同的Discovery选择,内置的实现称为Zen Discovery,其封装了节点发现(ping).选主等实现过程.本文基于E ...

  2. 【Elasticsearch】zen discovery集群发现机制

    1.概述 转载:https://blog.csdn.net/yangshangwei/article/details/103996803 继续跟中华石杉老师学习ES,第64篇 课程地址: https: ...

  3. 【Es】Es 选主流程

    1.概述 本文转载并且进行补充:Es 选主流程 实例化node后调用各个模块的 start方法,discovery模块调用startInitialJoin()->startNewThreadIf ...

  4. 【Elasticsearch】留意Elasticsearch 7.x 可能无法选主的问题

    1.概述 转载:留意Elasticsearch 7.x 可能无法选主的问题 Elasticsearch 7.x 选举算法改为基于 Raft 的实现,与标准 Raft 相比,最大的区别是允许选民可以投多 ...

  5. 一文详解Elasticsearch中的Node角色以及使用方法

    前言 说到elasticsearch,大家第一反应就是他是一款NOSQL数据库,既然是NOSQL数据库,则生产环境上必定是集群,由很多台服务器共同搭建而成.按照常理,分布式集群从搭建模式上分为中心化模 ...

  6. 教你如何在 elasticsearch 中重建索引

    序言 Elasticsearch 是一个实时的分布式搜索分析引擎.Teambition 使用 Elastisearch 作为搜索引擎,为用户提供搜索服务,当我们决定存储某种数据时,我们需要使用PUT ...

  7. 【ElasticSearch】大数据搜索选开源还是商业软件?ElasticSearch 对比 Splunk

    1.概述 转载:大数据搜索选开源还是商业软件?ElasticSearch 对比 Splunk述 本文就架构,功能,产品线,概念等方面就ElasticSearch和Splunk做了一下全方位的对比,希望 ...

  8. 在Elasticsearch中回测隨機(Stochastic)指標交叉交易策略

    之前的文章"在Elasticsearch 中回测 RSI 交叉策略",介紹在Elasticsearch中如何回測 相对强弱指数(RSI)指标交叉交易策略.在本文中,我们将实施随机( ...

  9. 在Elasticsearch中回测阿隆(Aroon)指標交叉交易策略

    我们已经讨论过如何在Elasticsearch 中回测" RSI 交叉策略"和 随机(Stochastic)交叉策略,在本文中,我们将实现阿隆(Aroon)交叉策略,并将其性能与上 ...

最新文章

  1. [Skr-Shop]购物车之架构设计
  2. mysql order by 多字段排序
  3. Mongodb-初步了解
  4. bootstrap 获取表格修改的结果_bootstrap-table前端修改后台传来的数据重新进行渲染...
  5. Maven教程详细总结+学习路线
  6. VTK:PolyData之GetPoint
  7. 记一次 javax.xml.soap.SOAPException:
  8. stm32学习笔记----双串口同时打开时的printf()问题
  9. http:(1):http简介
  10. 美颜相机window 开源_X-Window系统| 免费和开源软件
  11. JeecgBoot 移动OA 新版本出炉,速度体验!!!
  12. 定量的方法如何能划分类别?
  13. Python批量导入图片生成能治疗颈椎病的HTML5版课件
  14. PHP包管理器PEAR 中爆多个缺陷可发动供应链攻击,已潜伏15年
  15. oracle join(比较全面的解释了join)
  16. 详解iOS打包、发布与证书体系,深入解析证书非对称加密原理 知其所以然
  17. 麟龙指标通达信指标公式源码_麟龙指标通达信指标公式源码
  18. 英语词根与说文解字词典读书笔记,并总结输出思维导图
  19. Unity3d 游戏汉化之IL注入文本替换--木石世纪
  20. 什么专业可以留学计算机动画,美国留学计算机动画专业怎么样?

热门文章

  1. 计算机毕业设计基于Android宠物领养救助系统app
  2. Django Admin 上传多张图片并显示缩略图
  3. CVPR2020-对偶回归与SISR | Closed-loop Matters:Dual Regression Networks for Single Image Super-Resolution
  4. html中如何调整图片的对比色,最好的在线配色器 网页配色 在线配色
  5. 软件测试行业就业前景到底怎么样?
  6. 微信小程序--放入个性化手绘地图具体步骤(腾讯地图)
  7. 含有js的英文单词_js-组成-dom-常见单词
  8. [IDA Plugin] IDA插件收集
  9. 嵌入式论文分析:Energy-Efficient and Robust Middleware Prototyping for Smart Mobile Computing
  10. 智慧物联下主机加固如何理解?