「Elasticsearchのソースコード分析と最適化の実戦」第5章:主な選定プロセス - Motianlun
1. はじめに
Discovery モジュールは、クラスター内のノードを検出し、マスター ノードを選択する役割を果たします。ES はさまざまな検出タイプをサポートしており、組み込み実装は と呼ばれますZen Discovery
。その他には、パブリック クラウド プラットフォーム上の Amazon の EC2、Google の GCE などが含まれます。
この章では、組み込みの Zen Discovery 実装について説明します。Zen Discovery は、ノード検出 (Ping)、マスター選出などの実装プロセスをカプセル化します。ここでは、最初にマスター選出プロセスについて説明し、次の章で Discovery モジュール全体を紹介します。
2. デザイン思考
すべての分散システムは、何らかの方法で一貫性に対処する必要があります。一般に、戦略は 2 つのグループに分類できます。
-
矛盾を避けるように努める
-
そして、定義の不一致が発生した後にそれらを調整する方法。後者は、適用可能なシナリオでは非常に強力ですが、データ モデルには比較的厳しい制限があります。ここでは前者について説明し、ネットワーク障害に対処する方法を説明します。
3. マスター/スレーブ モードを使用する理由
マスター/スレーブ (リーダー/フォロワー) モードに加えて、もう 1 つのオプションは分散ハッシュ テーブル (DHT) です。これは、1 時間あたり数千のノードの離脱と参加をサポートでき、理解できない異種ネットワークでも使用できます。基礎となるネットワーク トポロジ 作業の途中では、クエリの応答時間は約 4 ~ 10 ホップ (通過時間) になります。たとえば、Cassandra はこのスキームを使用します。ただし、比較的安定したピアツーピア ネットワークでは、マスター/スレーブ モードの方が優れています。
ES の典型的なシナリオにおけるもう 1 つの単純化は、クラスター内のノードがそれほど多くないことです。通常、ノードの数は 1 つのノードが維持できる接続の数よりもはるかに少ないため、ネットワーク環境ではノードの頻繁な参加と離脱に対処する必要がありません。これが、マスター/スレーブ モードが ES に適している理由です。
4. 選出アルゴリズム
マスターノード選出アルゴリズムの選択における基本原則は、車輪の再発明を行わないことです。利点と欠点がわかっている、よく知られたアルゴリズムを実装することをお勧めします。ES 選出アルゴリズムの選択は主に以下の 2 種類を考慮します。
4.1. いじめアルゴリズム
リーダー選出の基本アルゴリズムの 1 つ。すべてのノードが一意の ID を持っていると想定し、その ID を使用してノードを並べ替えます。現在のリーダーはいつでも、クラスターに参加している最も高い ID ノードになります。 このアルゴリズムの利点は、実装が簡単であることです。ただし、最大の ID を持つノードが不安定な状態にある場合には問題が発生します。たとえば、マスターが過負荷でアニメーションが一時停止され、クラスター内で 2 番目に大きい ID を持つノードが新しいマスターとして選出されます。このとき、元のマスターは回復し、再び新しいマスターとして選出され、その後停止します。仮死状態…
ES は、現在のマスター ノードがハングアップしない限り、マスターが再選出されることはなく、現在のマスターが失敗するまで選出を延期することで上記の問題を解決します。ただし、スプリットブレイン(デュアルマスター)が発生しやすいため、「法定投票の半分以上」でスプリットブレインの問題は解決されます。
4.2. Paxos アルゴリズム
Paxos は非常に強力で、特にいつ、どのように選挙を実施するかについての柔軟性は、単純な Bully アルゴリズムよりも大きな利点があります。現実には、ネットワーク接続の異常よりも多くの障害モードがあるためです。しかし、Paxos の実装は非常に複雑です。
五、関連構成
マスター選出プロセスに関連する重要な構成には次のものが含まれますが、すべての構成が含まれるわけではありません。
discovery.zen.minimum_master_nodes
: マスター ノードの最小数。スプリット ブレインとデータ損失を防ぐために非常に重要なパラメータです。このパラメータの実際の効果は、すでにその表面的な意味を超えています。リーダーを選ぶ際の「多数決」を決定するために使用されるほか、少なくとも次のタイミングを含む多くの重要な判断にも使用されます。
-
リーダー選出のトリガー: リーダー選出プロセスに入る前に、参加ノードの数が定足数に達する必要があります。
-
マスターの決定: 一時マスターが選択された後、一時マスターは、マスター選択の成功を確認する前に、参加するノードの数がクォーラムに達していると判断する必要があります。
-
ゲートウェイ選出メタ情報: マスター資格を持つノードに対してメタデータを取得するリクエストを開始します。取得される応答の数はクォーラム、つまりメタ情報選出に参加しているノードの数に達する必要があります。
-
マスターがクラスターのステータスをリリースします。 成功したリリースの数が過半数を占めています。
スプリット ブレインを回避するには、その値が半分 (クォーラム) より大きい必要があります。(master_eligible_nodes 2)+1
例如:如果有3个具备Master资格的节点,则这个值至少应该设置为(3/2) + 1=2。
该参数可以动态设置:
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 に設定すると、マスター選出フェーズはマスター資格を持たないノード (node.master: false) からの ping リクエストを無視します。デフォルトは false です。 -
discovery.zen.fd.ping_interval
: 障害検出間隔期間。デフォルトは 1 秒です。 -
discovery.zen.fd.ping_timeout
: 障害検出要求のタイムアウト。デフォルトは 30 秒です。 -
discovery.zen.fd.ping_retries
: 障害検出タイムアウト後のリトライ回数。デフォルトは 3 回です。
6. プロセス分析
6.0. プロセスの概要
6.0.1. ZenDiscovery のマスター選択プロセスは次のとおりです。
-
各ノードは、既知の最小のノード ID (一時的なマスター)を計算します。このノードにリーダーシップ投票を送信します。
-
ノードが十分な投票を受け取り、ノード自体にも投票すると、そのノードはリーダーの役割を引き受け、クラスター状態の公開を開始します。
-
すべてのノードが選挙に参加して投票しますが、マスターになる資格がある (node.maste が true) ノードの投票のみが有効です。
選挙に勝つために何票獲得できるかが、いわゆる定足数です。ES では、クォーラム サイズは構成可能なパラメーターです。設定項目: discovery.zen.minimum master_ nodes
。スプリット ブレインを回避するには、最小値をマスター認定ノードの数にする必要がありますn/2+1
。
6.0.2. 全体的なプロセスは次のように要約できます。
-
一時的なマスターの選出;
-
投票 - マスターを確認し、ノードが選出された場合はマスターの確立を待ち、他のノードが選出された場合はクラスターへの参加を試み、ノード障害検出器を開始します。
-
障害ノードの検出。
詳細を以下の図に示します。
このプロセスを実行するためのスレッド プール: 汎用。
以下では、各ステップの実装を詳細に分析します。
6.1. 一時マスターの選出
選出プロセスの実装は にありますZenDiscovery#findMaster
。この関数は、現在のクラスターのアクティブなマスターを検索するか、候補から新しいマスターを選択します。マスターの選択が成功した場合は選択されたマスターを返し、それ以外の場合は null を返します。
なぜ一時マスターなのか?まだ次のステップを待つ必要があるため、十分な投票が得られた時点でノードは真のマスターとして確立されます。
暫定マスターの選出プロセスは次のとおりです。
-
すべてのノードを「Ping」し、ノード リスト fullPingResponses を取得します。ping 結果にはこのノードは含まれません。このノードを fullPingResponses に個別に追加します。
-
2 つのリストを作成します。
-
activeMasters list: ストレージ クラスターの現在のアクティブなマスター リスト。
-
masterCandidates list: マスター候補のリストを保存します。
-
activeMasters list: ストレージ クラスターの現在のアクティブなマスター リスト。 最初のステップで取得したすべてのノードを調べ、各ノードが考慮する現在のマスター ノードを activeMasters リストに追加します (このノードを除く)。トラバーサル プロセス中に、構成discovery.zen.master_election.ignore_non_master_pings
が true (デフォルトは false) で、ノードにマスター資格がない場合、ノードはスキップされます。
具体的なプロセスを下図に示します。
ここに画像の説明を挿入
このプロセスでは、クラスター内に現在存在するマスターを activeMasters リストに追加します。通常は 1 つだけ存在します。クラスターに既にマスターがある場合、各ノードは現在のマスターを記録しますが、異常事態を考慮すると、各ノードは異なる現在のマスターを認識する可能性があります。activeMasters リストを作成するプロセスで、ノードにマスター資格がない場合は、ignore_non_master_pings
オプションを考慮してマスターを無視できます。
masterCandidates list: マスター候補のリストを格納します。 最初のステップを実行してリストを取得し、マスター資格を持たないノードを削除して、このリストに追加します。activeMasters が空の場合、選挙は masterCandidates から行われ、選挙は成功する場合も失敗する場合もあります。空でない場合は、activeMasters から最適なものをマスターとして選択します。
全体的なプロセスを次の図に示します。
ここに画像の説明を挿入
6.1.1.masterCandidates
マスターを選択します
マスター選択の具体的な詳細の実装は、たとえば、十分な候補があるかどうかの判断、特定のノードをマスターとして選択するなど、ElectMasterService クラスにカプセル化されます。
masterCandidates からマスターを選択するときは、まず現在の候補者数が定足数に達しているかどうかを判断する必要があります。定足数に達していない場合、マスターの選択は失敗します。
public boolean hasEnoughCandidates (Collection<MasterCandidate> candidates) {
//候选者为空,返回失败
if (candidates.isEmpty()) {
returnfalse;
}
//默认值为-1, 确保单节点的集群可以正常选主
if (minimumMasterNodes < 1) {
returntrue;
}
return candidates .size () >= minimumMasterNodes;
}
候補者の数が定足数に達したら、マスターとなる候補者の 1 人を選択します。
public MasterCandidate electMaster (Collection<MasterCandidate> candidates) {
List<MasterCandidate> sortedCandidates = new ArrayList<> (candidates);
//通过自定义的比较函数对候选者节点从小到大排序
sortedCandidates.sort (MasterCandidate :: compare);
//返回最新的作为Master
return sortedCandidates.get(0);
}
ここでは、ノードをソートした後、最小のノードのみがマスターとして選択されていることがわかります。ただし、ソートにはカスタム比較関数 MasterCandidate::compare が使用されます。以前のバージョンでは、ノード ID のみがソートされていました。現在は、クラスター状態のバージョン番号が高いノードが最初に優先されます。
デフォルトの比較関数を使用した場合、ソート結果は小さい順にソートされます。Long 型の比較関数の実装を参照してください。
public static int compare (1ong X,long y) {
return(x<y)?-1:((x==y)?0:1);
}
自定义比较函数的实现:
public static int compare(MasterCandidate cl, MasterCandidate c2) {
//先比较集群状态版本,注意此处c2在前,c1在后
int ret = Long.compare(c2.clusterStateVersion, c1.clusterStateVersion);
//如果版本号相同,则比较节点ID
if(ret==0){
ret = compareNodes (c1.getNode(),c2.getNode());
}
return ret;
}
ノード比較関数compareNodesの実装: ソート効果用
-
2 つの受信ノードのうちの 1 つがマスター資格を持っており、もう 1 つがマスター資格を持っていない場合、マスター資格のあるノードが最初にランク付けされます。
-
どちらもマスター資格を持っていない場合、または両方ともマスター資格を持っている場合は、ノード ID を比較します。
ただし、masterCandidates リスト内のノードはすべて Master の資格があります。CompareNodes 比較関数の if 判定が 2 つあるのは、ノード リスト内に他の関数呼び出しで 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 リストからの選択リストには、クラスター内の現在アクティブなマスターが保存され、これらの既知のマスター ノードの 1 つが選択結果として選択されます。選択プロセスは非常に単純で、リスト内の最小値を取得します。比較関数は依然として CompareNodes を通じて実装され、activeMasters リスト内のノードは理論的にはマスターとして認定されます。
public DiscoveryNode tieBreakActiveMasters (Collection<DiscoveryNode> activeMasters)
return activeMasters.stream().min(ElectMasterService ::compareNodes).get();
}
6.1.2. 投票と投票の実現
ES では、投票の送信は JoinRequest リクエストの送信となります。投票は、ノードへの参加リクエストの数です。投票の収集と統計の作成の実装はZenDiscovery#handleJoinRequest
メソッド内にあり、受信した接続は に保存されますElectionContext#joinRequestAccumulator
。ノードは受信した投票が十分であるかどうかをチェックするときに、ノードに参加している接続の数が十分であるかどうかをチェックし、マスター資格のないノードの投票は削除されます。
public synchronized int getPendingMasterJoinsCount() {
int pendingMasterJoins = 0;
//遍历当前收到的join请求
for (DiscoveryNode node : joinReques tAccumulator .keySet()) {
//过滤不具备master资格的节点
if (node. isMasterNode()) {
pendingMasterJoins++;
}
}
return pendingMasterJoins;
}
6.1.3. マスターを確立するかクラスターに参加する
選択された一時マスターには 2 つの状況があります。一時マスターがローカル ノードである場合と、非ローカル ノードである場合です。これは別個に処理されます。これで、投票を送信する準備ができました。
6.1.3.1. 一時的なマスターが現在のノードの場合:
-
十分な数のマスター認定ノードがこのノードに参加するまで (投票がクォーラムに達するまで)、選挙が完了するまで待ちます。タイムアウト (デフォルトは 30 秒、構成可能) 後に参加リクエストの数が満たされない場合、選出は失敗し、新たな選出が必要になります。
-
成功したら新しいクラスター状態をポストします。
6.1.3.2. 他のノードがマスターとして選択されている場合:
-
他のノードからの参加リクエストは受け付けられなくなりました。
-
参加リクエストをマスターに送信し、応答を待ちます。タイムアウトのデフォルトは 1 分 (構成可能) で、例外が発生した場合はデフォルトで 3 回再試行されます (構成可能)。このステップは joinElectedMaster メソッドで実装されます。
-
最終的に選出されたマスターは、クライアントの参加要求を確認する前にクラスターのステータスを公開するため、joinElectedMaster は参加要求が受信され、クラスターのステータスが受信されたことを示す確認を返します。このステップでは、受信したクラスター ステータスのマスター ノードが空であるかどうかを確認するか、選択されたマスターが以前に選択されたノードではない場合は再選択します。
6.2. ノード障害の検出
これまでのところ、マスター選択プロセスは完了し、マスター ID が確認され、非マスター ノードがクラスターに参加しました。ノード障害検知では、ノードがオフラインかどうかを監視し、異常に対処します。障害検出はマスター選択プロセスの後に不可欠なステップであり、障害検出を実行しないとスプリットブレイン (デュアルまたは複数のマスター) が発生する可能性があります。ここでは 2 つの障害検出機能を有効にする必要があります。
-
マスター ノードで、
NodesFaultDetection
NodesFD と呼ばれる を開始します。クラスターに参加しているノードがアクティブかどうかを定期的に検出します。 -
非マスター ノードでは、
MasterFaultDetection
MasterFD と呼ばれる を開始します。マスターノードがアクティブかどうかを定期的に検出します。
NodesFaultDetection、MasterFaultDetectionともに、定期的(デフォルトでは1秒)に送信されるpingリクエストによりノードが正常かどうかを検出し、障害が一定回数(デフォルトでは3回)に達した場合、またはノードオフライン通知を受信した場合に処理を開始します。基礎となる接続モジュールのノード離脱イベントから。
6.2.1、NodesFaultDetection イベント処理
現在のクラスタ内の総ノード数が法定ノード数(半分以上)に達しているか確認し、満たしていない場合はマスターステータスを放棄し、クラスタに再参加します。 これを行う理由は何ですか? 以下の図に示すようなシナリオを想像してください。
ここに画像の説明を挿入
5 台のマシンで構成されるクラスタでネットワーク パーティションが生成され、2 台のマシンが 1 つのグループを形成し、他の 3 台のマシンが 1 つのグループを形成するとします。分割が発生する前の元のマスターは Node1 です。この時点で、3 つのノードのグループが再選出され、Noded3 がマスターとして正常に選択されます。デュアル マスターは存在しますか? NodesFaultDetection
上記のシナリオではデュアル マスターを回避するためです。
対応するイベント処理は主に次のように実装されます。でZenDiscovery#handleNodeFailure
実行されますNodeRemoval-ClusterStateTaskExecutor#execute
。
public ClusterTasksResult<Task> execute (final ClusterState currentState, final List<Task> 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)));
}
}
マスター ノードは、ノードがオフラインであることを検出し、現在のクラスター内のノード数がクォーラムに対して不十分であると判断した場合、デュアル マスターを避けるためにマスター ステータスを放棄します。
6.2.2、MasterFaultDetectionイベント処理
**マスターがオフラインであることを検出するプロセスは非常に簡単で、クラスターに再参加します。**基本的に、ノードはマスターを選択するプロセスを再実行します。対応するイベント処理は主に次のように実装されます。 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 + ")");
}
}
}
まとめ
クラスタ内でマスタ選出処理が開始され、マスタが存在しない状態から新たなマスタを生成するまでの処理が実行されますが、同時にクラスタの通常運用中に、マスタがノードの離脱を検知し、非マスターノードはマスターが離脱したことを検出します。