Elasticsearch重要知識(shí)點(diǎn) | 選舉流程詳解
點(diǎn)擊上方藍(lán)色字體,選擇“設(shè)為星標(biāo)”

選舉時(shí)間點(diǎn)
Elasticsearch在滿足如下時(shí)間點(diǎn)的時(shí)候會(huì)觸發(fā)選舉
集群啟動(dòng)初始化
集群的Master崩潰的時(shí)候
任何一個(gè)節(jié)點(diǎn)發(fā)現(xiàn)當(dāng)前集群中的Master節(jié)點(diǎn)沒有得到n/2 + 1節(jié)點(diǎn)認(rèn)可的時(shí)候,觸發(fā)選舉
選舉流程圖
ES選舉最核心的是Elasticsearch的選舉流程,筆者研究了Elasticsearch選舉源代碼,同時(shí)看了很多文章之后,梳理出了選舉過程中各個(gè)流程要點(diǎn),下圖是elasticsearch選舉的流程圖

接下來筆者一一介紹elasticsearch選舉的各個(gè)階段
1. 篩選activeMasters列表
Es的master就是從activeMasters列表或者masterCandidates列表選舉出來,所以選舉之前es首先需要得到這兩個(gè)列表。Elasticsearch節(jié)點(diǎn)成員首先向集群中的所有成員發(fā)送Ping請(qǐng)求,elasticsearch默認(rèn)等待discovery.zen.ping_timeout時(shí)間,然后elasticsearch針對(duì)獲取的全部response進(jìn)行過濾,篩選出其中activeMasters列表,activeMaster列表是其它節(jié)點(diǎn)認(rèn)為的當(dāng)前集群的Master節(jié)點(diǎn)
源代碼如下
List activeMasters = new ArrayList<>();
for (ZenPing.PingResponse pingResponse : pingResponses) {
//不允許將自己放在activeMasters列表中
if (pingResponse.master() != null && !localNode.equals(pingResponse.master())) {
activeMasters.add(pingResponse.master());
}
} 可以看到elasticsearch在獲取activeMasters列表的時(shí)候會(huì)排除本地節(jié)點(diǎn),目的是為了避免腦裂,假設(shè)這樣一個(gè)場景,當(dāng)前最小編號(hào)的節(jié)點(diǎn)P0認(rèn)為自己就是master并且P0和其它節(jié)點(diǎn)發(fā)生網(wǎng)絡(luò)分區(qū),同時(shí)es允許將自己放在activeMaster中,因?yàn)镻0編號(hào)最小,那么P0永遠(yuǎn)會(huì)選擇自己作為master節(jié)點(diǎn),那么就會(huì)出現(xiàn)腦裂的情況
2. 篩選masterCandidates列表
masterCandidates列表是當(dāng)前集群有資格成為Master的節(jié)點(diǎn),如果我們?cè)趀lasticsearch.yml中配置了如下參數(shù),那么這個(gè)節(jié)點(diǎn)就沒有資格成為Master節(jié)點(diǎn),也就不會(huì)被篩選進(jìn)入masterCandidates列表
# 配置某個(gè)節(jié)點(diǎn)沒有成為master資格
node.master:false源代碼如下所示
List masterCandidates = new ArrayList<>();
for (ZenPing.PingResponse pingResponse : pingResponses) {
if (pingResponse.node().isMasterNode()) {
masterCandidates.add(new ElectMasterService.MasterCandidate(pingResponse.node(), pingResponse.getClusterStateVersion()));
}
} 3. 從activeMasters列表選舉Master節(jié)點(diǎn)
activeMaster列表是其它節(jié)點(diǎn)認(rèn)為的當(dāng)前集群的Master節(jié)點(diǎn)列表,如果activeMasters列表不為空,elasticsearch會(huì)優(yōu)先從activeMasters列表中選舉,也就是對(duì)應(yīng)著流程圖中的藍(lán)色框,選舉的算法是Bully算法,筆者在前文中詳細(xì)介紹了Bully算法,Bully算法會(huì)涉及到優(yōu)先級(jí)比較, 在activeMasters列表優(yōu)先級(jí)比較的時(shí)候,如果節(jié)點(diǎn)有成為master的資格,那么優(yōu)先級(jí)比較高,如果activeMaster列表有多個(gè)節(jié)點(diǎn)具有master資格,那么選擇id最小的節(jié)點(diǎn)
代碼如下
private static int compareNodes(DiscoveryNode o1, DiscoveryNode o2) {
if (o1.isMasterNode() && !o2.isMasterNode()) {
return -1;
}
if (!o1.isMasterNode() && o2.isMasterNode()) {
return 1;
}
return o1.getId().compareTo(o2.getId());
}
public DiscoveryNode tieBreakActiveMasters(Collection activeMasters) {
return activeMasters.stream().min(ElectMasterService::compareNodes).get();
} 4. 從masterCandidates列表選舉Master節(jié)點(diǎn)
這一節(jié)對(duì)應(yīng)的是紅色流程圖中紅色部分,如果activeMaster列表為空,那么會(huì)在masterCandidates中選舉,masterCandidates選舉也會(huì)涉及到優(yōu)先級(jí)比較,masterCandidates選舉的優(yōu)先級(jí)比較和masterCandidates選舉的優(yōu)先級(jí)比較不同。它首先會(huì)判斷masterCandidates列表成員數(shù)目是否達(dá)到了最小數(shù)目discovery.zen.minimum_master_nodes。如果達(dá)到的情況下比較優(yōu)先級(jí),優(yōu)先級(jí)比較的時(shí)候首先比較節(jié)點(diǎn)擁有的集群狀態(tài)版本編號(hào),然后再比較id,這一流程的目的是讓擁有最新集群狀態(tài)的節(jié)點(diǎn)成為master
public static int compare(MasterCandidate c1, MasterCandidate c2) {
int ret = Long.compare(c2.clusterStateVersion, c1.clusterStateVersion);
if (ret == 0) {
ret = compareNodes(c1.getNode(), c2.getNode());
}
return ret;
}5. 本地節(jié)點(diǎn)是master
經(jīng)過上述選舉之后,會(huì)選舉出一個(gè)準(zhǔn)master節(jié)點(diǎn), 準(zhǔn)master節(jié)點(diǎn)會(huì)等待其它節(jié)點(diǎn)的投票,如果有discovery.zen.minimum_master_nodes-1個(gè)節(jié)點(diǎn)投票認(rèn)為當(dāng)前節(jié)點(diǎn)是master,那么選舉就成功,準(zhǔn)master會(huì)等待discovery.zen.master_election.wait_for_joins_timeout時(shí)間,如果超時(shí),那么就失敗。在代碼實(shí)現(xiàn)上準(zhǔn)master通過注冊(cè)一個(gè)回調(diào)來實(shí)現(xiàn),同時(shí)借助了AtomicReference和CountDownLatch等并發(fā)構(gòu)建實(shí)現(xiàn)
if (clusterService.localNode().equals(masterNode)) {
final int requiredJoins = Math.max(0, electMaster.minimumMasterNodes() - 1);
nodeJoinController.waitToBeElectedAsMaster(requiredJoins, masterElectionWaitForJoinsTimeout,
new NodeJoinController.ElectionCallback() {
@Override
public void onElectedAsMaster(ClusterState state) {
joinThreadControl.markThreadAsDone(currentThread);
nodesFD.updateNodesAndPing(state); // start the nodes FD
}
@Override
public void onFailure(Throwable t) {
logger.trace("failed while waiting for nodes to join, rejoining", t);
joinThreadControl.markThreadAsDoneAndStartNew(currentThread);
}
}
);本地節(jié)點(diǎn)是Master的時(shí)候,Master節(jié)點(diǎn)會(huì)開啟錯(cuò)誤檢測(cè)(NodeFaultDetection機(jī)制),它節(jié)點(diǎn)會(huì)定期掃描集群所有的成員,將失活的成員移除集群,同時(shí)將最新的集群狀態(tài)發(fā)布到集群中,集群成員收到最新的集群狀態(tài)后會(huì)進(jìn)行相應(yīng)的調(diào)整,比如重新選擇主分片,進(jìn)行數(shù)據(jù)復(fù)制等操作
6. 本地節(jié)點(diǎn)不是master
當(dāng)前節(jié)點(diǎn)判定在集群當(dāng)前狀態(tài)下如果自己不可能是master節(jié)點(diǎn),首先會(huì)禁止其他節(jié)點(diǎn)加入自己,然后投票選舉出準(zhǔn)Master節(jié)點(diǎn)。同時(shí)監(jiān)聽master發(fā)布的集群狀態(tài)(MasterFaultDetection機(jī)制),如果集群狀態(tài)顯示的master節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn)認(rèn)為的master節(jié)點(diǎn)不是同一個(gè)節(jié)點(diǎn),那么當(dāng)前節(jié)點(diǎn)就重新發(fā)起選舉。
非Master節(jié)點(diǎn)也會(huì)監(jiān)聽Master節(jié)點(diǎn)進(jìn)行錯(cuò)誤檢測(cè),如果成員節(jié)點(diǎn)發(fā)現(xiàn)master連接不上,重新加入新的Master節(jié)點(diǎn),如果發(fā)現(xiàn)當(dāng)前集群中有很多節(jié)點(diǎn)都連不上master節(jié)點(diǎn),那么會(huì)重新發(fā)起選舉。

版權(quán)聲明:
文章不錯(cuò)?點(diǎn)個(gè)【在看】吧!??




