Elasticsearch中的Zen Discovery选主流程
文章目录
- 背景
- 为什么使用主从模式?
- 选举算法
- 什么时候触发选主?
- 选主过程
- 选举临时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论文实现,而是做了一些调整。
什么时候触发选主?
主要在以下三个场景会触发选主:
- 集群启动初始化;
- 集群的Master崩溃的时候;
- 任何一个节点发现当前集群中的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的选举过程如下:
“ping”所有节点,获取节点列表fullPingResponses,ping结果不包含本节点,把本节点单独添加到fullPingResponses中。
构建两个列表。activeMasters列表:存储集群当前活跃Master列表。遍历第一步获取的所有节点,将每个节点所认为的当前Master节点加入activeMasters列表中(不包括本节点)。在遍历过程中,如果配置了discovery.zen.master_election.ignore_non_master_pings为true(默认为false),而节点又不具备Master资格,则跳过该节点。masterCandidates列表:存储master候选者列表。遍历第一步获取列表,去掉不具备Master资格的节点,添加到这个列表中。
如果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是本节点:
- 等待足够多的具备Master资格的节点加入本节点(投票达到法定人数),以完成选举。
- 超时(默认为30秒,可配置)后还没有满足数量的join请求,则选举失败,需要进行新一轮选举。
- 成功后发布新的clusterState.
如果其他节点被选为Master:
- 不再接受其他节点的join请求。
- 向Master发送加入请求,并等待回复。超时时间默认为1分钟(可配置),如果遇到异常,则默认重试3次(可配置)。这个步骤在joinElectedMaster方法中实现。
- 最终当选的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选主流程相关推荐
- 【Elasticsearch选主流程】
Discovery模块负责发现集群中的节点,以及选择主节点.ES支持多种不同的Discovery选择,内置的实现称为Zen Discovery,其封装了节点发现(ping).选主等实现过程.本文基于E ...
- 【Elasticsearch】zen discovery集群发现机制
1.概述 转载:https://blog.csdn.net/yangshangwei/article/details/103996803 继续跟中华石杉老师学习ES,第64篇 课程地址: https: ...
- 【Es】Es 选主流程
1.概述 本文转载并且进行补充:Es 选主流程 实例化node后调用各个模块的 start方法,discovery模块调用startInitialJoin()->startNewThreadIf ...
- 【Elasticsearch】留意Elasticsearch 7.x 可能无法选主的问题
1.概述 转载:留意Elasticsearch 7.x 可能无法选主的问题 Elasticsearch 7.x 选举算法改为基于 Raft 的实现,与标准 Raft 相比,最大的区别是允许选民可以投多 ...
- 一文详解Elasticsearch中的Node角色以及使用方法
前言 说到elasticsearch,大家第一反应就是他是一款NOSQL数据库,既然是NOSQL数据库,则生产环境上必定是集群,由很多台服务器共同搭建而成.按照常理,分布式集群从搭建模式上分为中心化模 ...
- 教你如何在 elasticsearch 中重建索引
序言 Elasticsearch 是一个实时的分布式搜索分析引擎.Teambition 使用 Elastisearch 作为搜索引擎,为用户提供搜索服务,当我们决定存储某种数据时,我们需要使用PUT ...
- 【ElasticSearch】大数据搜索选开源还是商业软件?ElasticSearch 对比 Splunk
1.概述 转载:大数据搜索选开源还是商业软件?ElasticSearch 对比 Splunk述 本文就架构,功能,产品线,概念等方面就ElasticSearch和Splunk做了一下全方位的对比,希望 ...
- 在Elasticsearch中回测隨機(Stochastic)指標交叉交易策略
之前的文章"在Elasticsearch 中回测 RSI 交叉策略",介紹在Elasticsearch中如何回測 相对强弱指数(RSI)指标交叉交易策略.在本文中,我们将实施随机( ...
- 在Elasticsearch中回测阿隆(Aroon)指標交叉交易策略
我们已经讨论过如何在Elasticsearch 中回测" RSI 交叉策略"和 随机(Stochastic)交叉策略,在本文中,我们将实现阿隆(Aroon)交叉策略,并将其性能与上 ...
最新文章
- [Skr-Shop]购物车之架构设计
- mysql order by 多字段排序
- Mongodb-初步了解
- bootstrap 获取表格修改的结果_bootstrap-table前端修改后台传来的数据重新进行渲染...
- Maven教程详细总结+学习路线
- VTK:PolyData之GetPoint
- 记一次 javax.xml.soap.SOAPException:
- stm32学习笔记----双串口同时打开时的printf()问题
- http:(1):http简介
- 美颜相机window 开源_X-Window系统| 免费和开源软件
- JeecgBoot 移动OA 新版本出炉,速度体验!!!
- 定量的方法如何能划分类别?
- Python批量导入图片生成能治疗颈椎病的HTML5版课件
- PHP包管理器PEAR 中爆多个缺陷可发动供应链攻击,已潜伏15年
- oracle join(比较全面的解释了join)
- 详解iOS打包、发布与证书体系,深入解析证书非对称加密原理 知其所以然
- 麟龙指标通达信指标公式源码_麟龙指标通达信指标公式源码
- 英语词根与说文解字词典读书笔记,并总结输出思维导图
- Unity3d 游戏汉化之IL注入文本替换--木石世纪
- 什么专业可以留学计算机动画,美国留学计算机动画专业怎么样?
热门文章
- 计算机毕业设计基于Android宠物领养救助系统app
- Django Admin 上传多张图片并显示缩略图
- CVPR2020-对偶回归与SISR | Closed-loop Matters:Dual Regression Networks for Single Image Super-Resolution
- html中如何调整图片的对比色,最好的在线配色器 网页配色 在线配色
- 软件测试行业就业前景到底怎么样?
- 微信小程序--放入个性化手绘地图具体步骤(腾讯地图)
- 含有js的英文单词_js-组成-dom-常见单词
- [IDA Plugin] IDA插件收集
- 嵌入式论文分析:Energy-Efficient and Robust Middleware Prototyping for Smart Mobile Computing
- 智慧物联下主机加固如何理解?