ES版本5.6.3

1、整个流程的开始,实在node启动后触发的,Node.java中start()方法,通过调用ZenDiscovery.java中的doStart()方法,之后会调用startInitialJoin方法开始进行加入现有的cluster或者选主。

public void startInitialJoin() {
// start the join thread from a cluster state update. See {@link JoinThreadControl} for details.
clusterService.submitStateUpdateTask("initial_join", new LocalClusterUpdateTask() { @Override
public ClusterTasksResult<LocalClusterUpdateTask> execute(ClusterState currentState) throws Exception {
// do the join on a different thread, the DiscoveryService waits for 30s anyhow till it is discovered
joinThreadControl.startNewThreadIfNotRunning();
return unchanged();
} @Override
public void onFailure(String source, @org.elasticsearch.common.Nullable Exception e) {
logger.warn("failed to start initial join process", e);
}
});
}

2、ZenDiscovery类中startNewThreadIfNotRunning方法中innerJoinCluster()为实质性进行选主操作,其中findMaster()选择master节点。

private void innerJoinCluster() {
DiscoveryNode masterNode = null;
final Thread currentThread = Thread.currentThread();
nodeJoinController.startElectionContext();
while (masterNode == null && joinThreadControl.joinThreadActive(currentThread)) {
masterNode = findMaster();
} ......
}

3、在findMaster()中。通过pingAndWait()方法获取当前可以ping通的节点,并获取PingResponse,此信息中包含节点信息以及该节点当前的master节点信息。之后,根据获取的节点开始进行选主。

  此处有一参数需要注意:discovery.zen.master_election.ignore_non_master_pings,默认值为false,表明数据节点(node.master: false    node.data: true)是否参与选主,一般我们集群节点数较少时,不用修改此配置,如果集群规模很大,可以考虑只允许主节点参与选主操作。

  然后,根据获取的pingResponses来判断当前是否有master节点存在,存储在activeMasters中,对于master的候选节点存储在masterCandidates中。

  如果activeMasters为空,表明当前并未有master节点存在,则进行选主操作,即步骤4。这里需要注意的是discovery.zen.minimum_master_nodes,候选节点数必选大于等于该参数,选主才能继续,否则是无法选主的。该参数一般配置为(N/2)+1,防止集群出现脑裂。

  如果activeMasters不为空,则当前的master节点,即为步骤5中的找到的master节点。

 private DiscoveryNode findMaster() {
logger.trace("starting to ping");
List<ZenPing.PingResponse> fullPingResponses = pingAndWait(pingTimeout).toList();
if (fullPingResponses == null) {
logger.trace("No full ping responses");
return null;
}
if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder();
if (fullPingResponses.size() == 0) {
sb.append(" {none}");
} else {
for (ZenPing.PingResponse pingResponse : fullPingResponses) {
sb.append("\n\t--> ").append(pingResponse);
}
}
logger.trace("full ping responses:{}", sb);
} final DiscoveryNode localNode = clusterService.localNode(); // add our selves
assert fullPingResponses.stream().map(ZenPing.PingResponse::node)
.filter(n -> n.equals(localNode)).findAny().isPresent() == false; fullPingResponses.add(new ZenPing.PingResponse(localNode, null, clusterService.state())); // filter responses
final List<ZenPing.PingResponse> pingResponses = filterPingResponses(fullPingResponses, masterElectionIgnoreNonMasters, logger); List<DiscoveryNode> activeMasters = new ArrayList<>();
for (ZenPing.PingResponse pingResponse : pingResponses) {
// We can't include the local node in pingMasters list, otherwise we may up electing ourselves without
// any check / verifications from other nodes in ZenDiscover#innerJoinCluster()
if (pingResponse.master() != null && !localNode.equals(pingResponse.master())) {
activeMasters.add(pingResponse.master());
}
} // nodes discovered during pinging
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()) {
if (electMaster.hasEnoughCandidates(masterCandidates)) {
final ElectMasterService.MasterCandidate winner = electMaster.electMaster(masterCandidates);
logger.trace("candidate {} won election", winner);
return winner.getNode();
} else {
// if we don't have enough master nodes, we bail, because there are not enough master to elect from
logger.warn("not enough master nodes discovered during pinging (found [{}], but needed [{}]), pinging again",
masterCandidates, electMaster.minimumMasterNodes());
return null;
}
} else {
assert !activeMasters.contains(localNode) : "local node should never be elected as master when other nodes indicate an active master";
// lets tie break between discovered nodes
return electMaster.tieBreakActiveMasters(activeMasters);
}
}

4、ElectMasterService.java中的electMaster()方法为选主的具体实现,逻辑十分简单根据当前的候选节点进行排序,排在第一个的即为master节点。

public MasterCandidate electMaster(Collection<MasterCandidate> candidates) {
assert hasEnoughCandidates(candidates);
List<MasterCandidate> sortedCandidates = new ArrayList<>(candidates);
sortedCandidates.sort(MasterCandidate::compare);
return sortedCandidates.get(0);
}

5、当存在master节点时,则加入现有的集群中,如果是多个master节点,则会选择排在第一个的master节点作为需要加入的集群。

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

6、在选择完master节点后,需要进行集群组建了。如果当前选择出的master节点为本节点,则本节点需要等待其他节点来加入。这个逻辑不太确定,是异步实现的。大体如下:在每个node启动时,均会注册internal:discovery/zen/join请求,待其为maser后,其他节点通过该请求与之通信加入到master所在集群。在MembershipListener.java中注册监听,当有节点加入时,通过异步函数将信息存储在NodeJoinController下的内部类ElectionContext中,具体判断是否获取了足够的节点,判断当次选举是否成功。--------这块逻辑比较绕,不确定理解是否正确,如哪位大神比较了解,望不吝赐教啊!!!!

7、如果选择的master节点不是本节点,则选择加入该集群ZenDiscovery.java中joinElectedMaster(),尝试次数为discovery.zen.join_retry_attempts由控制,默认为3次,每次的超时时间:discovery.zen.join_timeout控制,默认值为discovery.zen.ping_timeout*20也就是60ms。所以这个参数不宜配置过长,否则在选举失败的超时时间就会比较长。如果加入master失败或者超时,则会进行新的一轮选主,直到选则出满足条件的master节点。

private boolean joinElectedMaster(DiscoveryNode masterNode) {
try {
// first, make sure we can connect to the master
transportService.connectToNode(masterNode);
} catch (Exception e) {
logger.warn((Supplier<?>) () -> new ParameterizedMessage("failed to connect to master [{}], retrying...", masterNode), e);
return false;
}
int joinAttempt = 0; // we retry on illegal state if the master is not yet ready
while (true) {
try {
logger.trace("joining master {}", masterNode);
membership.sendJoinRequestBlocking(masterNode, clusterService.localNode(), joinTimeout);
return true;
} catch (Exception e) {
final Throwable unwrap = ExceptionsHelper.unwrapCause(e);
if (unwrap instanceof NotMasterException) {
if (++joinAttempt == this.joinRetryAttempts) {
logger.info("failed to send join request to master [{}], reason [{}], tried [{}] times", masterNode, ExceptionsHelper.detailedMessage(e), joinAttempt);
return false;
} else {
logger.trace("master {} failed with [{}]. retrying... (attempts done: [{}])", masterNode, ExceptionsHelper.detailedMessage(e), joinAttempt);
}
} else {
if (logger.isTraceEnabled()) {
logger.trace((Supplier<?>) () -> new ParameterizedMessage("failed to send join request to master [{}]", masterNode), e);
} else {
logger.info("failed to send join request to master [{}], reason [{}]", masterNode, ExceptionsHelper.detailedMessage(e));
}
return false;
}
} try {
Thread.sleep(this.joinRetryDelay.millis());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}

至此,master就已经选择完成了。大概逻辑就是这样,可能中间一些细节有待进一步深究。

ES选主策略的更多相关文章

  1. 【Elasticsearch】ES选主流程分析

    Raft协议 Raft是分布式系统中的一种共识算法,用于在集群中选举Leader管理集群.Raft协议中有以下角色: Leader(领导者):集群中的领导者,负责管理集群. Candidate(候选者 ...

  2. kazoo python zookeeper 选主

    本文讲述基于zookeeper选主与故障切换的方法.我们的例子使用的是python. 使用的库是kazoo,安装方式 pip install kazoo  应用场景: 多个实例部署,但不是" ...

  3. zookeeper curator选主(Leader)

    在分布式系统设计中,选主是一个常见的场景.选主是一个这样的过程,通过选主,主节点被选择出来控制其他节点或者是分配任务. 选主算法要满足的几个特征: 1)各个节点均衡的获得成为主节点的权利,一旦主节点被 ...

  4. 聊聊Zookeeper应用场景、架构设计、选主机制

    Zookeeper作为一个分布式协调系统提供了一项基本服务:分布式锁服务,分布式锁是分布式协调技术实现的核心内容.像配置管理.任务分发.组服务.分布式消息队列.分布式通知/协调等,这些应用实际上都是基 ...

  5. Zookeeper笔记之使用zk实现集群选主

    一.需求 在主从结构的集群中,我们假设硬件机器是很脆弱的,随时可能会宕机,当master挂掉之后需要从slave中选出一个节点作为新的master,使用zookeeper可以很简单的实现集群选主功能. ...

  6. 源码分析 RocketMQ DLedger 多副本之 Leader 选主

    目录 1.DLedger关于选主的核心类图 1.1 DLedgerConfig 1.2 MemberState 1.3 raft协议相关 1.4 DLedgerRpcService 1.5 DLedg ...

  7. ETCD分布式锁实现选主机制(Golang实现)

    ETCD分布式锁实现选主机制(Golang) 为什么要写这篇文章 做架构的时候,涉及到系统的一个功能,有一个服务必须在指定的节点执行,并且需要有个节点来做任务分发,想了半天,那就搞个主节点做这事呗,所 ...

  8. kafka分区选主机制

    Kafka Partition Leader选主机制 https://blog.csdn.net/qq_27384769/article/details/80115392 kafka leader选举 ...

  9. 简述 zookeeper 基于 Zab 协议实现选主及事务提交

    Zab 协议:zookeeper 基于 Paxos 协议的改进协议 zookeeper atomic broadcast 原子广播协议. zookeeper 基于 Zab 协议实现选主及事务提交. 一 ...

随机推荐

  1. mysql 数据操作 单表查询 group by 分组 目录

    mysql 数据操作 单表查询 group by 介绍 mysql 数据操作 单表查询 group by 聚合函数 mysql 数据操作 单表查询 group by 聚合函数 没有group by情况 ...

  2. js-jquery-SweetAlert2【三】INPUT TYPES

    1.text swal({ title: 'Input something', input: 'text', showCancelButton: true, inputValidator: funct ...

  3. ROS 命令学习记录

    #catkin_init_workspace //just get CMakeList.txt #catkin_make  //增加了信文件夹 build和devel #Catkin_creat_pk ...

  4. Mybatis入门和简单Demo

    一.Mybatis的诞生 回顾下传统的数据库开发,JDBC和Hibernate是使用最普遍的技术,但这两种ORM框架都存在一定的局限性: JDBC:最原生的技术,简单易学,执行速度快,效率高,适合大数 ...

  5. docker——Etcd高可用键值对数据库

    一.简介 Etcd按照官方介绍: Etcd is a distributed, consistent key-value store for shared configuration and serv ...

  6. sql server 将时间中的时分秒改为00:00:00

    select convert(varchar(10),getdate(),120

  7. open-falcon api相关

    本文描述通过被监控endpoint的名称获取该endpoint的eid和监控项,从而获取到该endpoint的监控历史数据,使用python代码的 api操作方法 注:同步open-falcon和ag ...

  8. Python: 分数运算

    fractions 模块可以被用来执行包含分数的数学运算 >>> from fractions import Fraction >>> a = Fraction(5 ...

  9. SQL: coalesce()函数

    ①用途: 将空值替换成其他值 返回第一个非空值 ②表达式: COALESCE是一个函数, (expression_1, expression_2, ...,expression_n)依次参考各参数表达 ...

  10. mysql设置utf8方法

    转自:http://blog.csdn.net/u014657752/article/details/48206885 1. SET NAMES 'utf8'; 它相当于下面的三句指令:SET cha ...