Главный узел периодически отправляет эхо-запросы другим узлам в кластере, чтобы проверить, активен ли узел. обработать. Другие узлы также периодически проверяют, активен ли мастер.Если мастер не может быть восстановлен, узел повторно выполнит процесс выбора мастера.
Обнаружение узла и выбор мастера в кластере зависят от интерфейса обнаружения.Встроенная реализация ESZen Discovery
Основной избирательный алгоритм Elasticsearch — Bully
Количество узлов в кластере Elasticsearch ограничено, один узел может обрабатывать соединения со всеми другими узлами, а узлы не будут часто присоединяться и покидать кластер, поэтому Zen Discovery использует относительно простой алгоритм Bully.
В кластере есть мастер, который использует алгоритм Bully для хранения информации о состоянии всего кластера и регулярно передает информацию о версии состояния кластера другим узлам в кластере.Если алгоритм Bully используется напрямую, возникают следующие проблемы. будут обнаружены, поэтому Elasticsearch сделал специализацию обработки
Проблема 1: приостановленная анимация главного узла
Мастер может находиться в нестабильном состоянии из-за большой нагрузки и может не отвечать на запросы от некоторых узлов, но может вернуться в нормальное состояние за короткое время.Во избежание частых выборов ЭС использует метод отсрочки выборов пока мастер не выйдет из строя. Когда узел не получает ответа от мастера, он сначала запрашивает у других узлов список активных мастеров, а затем инициирует выборы после подтверждения того, что мастер умер.
Проблема 2: Разделенный мозг
Рассмотрим следующую ситуацию: исходный кластер состоит из A, B, C, D, E, а главный узел — это A. Когда соединение между двумя коммутаторами прерывается, A, B больше не могут взаимодействовать с C, D и E, образуя два сетевых раздела. В кластере, состоящем из A и B, ведущим по-прежнему является A, в то время как C, D и E выберут нового мастера.Когда клиент обращается к A, B или старшим C, D и E, данные несогласованны.
Elasticsearch принимает настройку «более половины голосов кворума».В процессе выборов, когда количество голосов узлов достигаетdiscovery.zen.minimum_master_nodes
может стать мастером только тогда, когда значение , которое обычно устанавливается равным(具有master资格节点数 / 2)+1
.
в приведенном выше примереdiscovery.zen.minimum_master_nodes
Установите на 3. Когда соединение между двумя коммутаторами прерывается, A больше не может обмениваться данными с C, D и E. В сетевом разделе, состоящем из C, D и E, нет активного мастера, поэтому инициируются выборы. В кластере A остались только A и B, а minimum_master_nodes не хватает, поэтому они отказываются от статуса master. C, D и E голосуют до тех пор, пока не будет достигнут консенсус по выбору мастера для формирования нового кластера, где число узлов равно 3, что соответствует минимальному_мастер_узлу, а A и B больше не обрабатывают запросы от клиентов.
основной избирательный процесс
пройти черезZenDiscovery::findMaster
Определяем временного мастера:
private DiscoveryNode findMaster() {
// 1. PING 所有节点,获取各节点保存的集群信息
List<ZenPing.PingResponse> fullPingResponses = pingAndWait(pingTimeout).toList();
// 2. 由于上面是获取的其他节点的信息,这里需要将本节点加上
final DiscoveryNode localNode = transportService.getLocalNode();
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) {
// 避免未经其他节点检查就将本节点选为 master
if (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. 没有活跃的 master
if (electMaster.hasEnoughCandidates(masterCandidates)) {
// 7. 拥有足够的候选节点,则进行选举
final ElectMasterService.MasterCandidate winner = electMaster.electMaster(masterCandidates);
return winner.getNode();
} else {
// 8. 无法选举,无法得到 master,返回 null
return null;
}
} else {
assert !activeMasters.contains(localNode) : "local node should never be elected as master when other nodes indicate an active master";
// 9. 有活跃的 master,从 activeMasters 中选择
return electMaster.tieBreakActiveMasters(activeMasters);
}
}
-
Используется, когда нет активного ведущего, описанный выше шаг 7 выполняет выбор ведущего путем
MasterCandidate::compare
Сравните узлы-кандидаты// ElectMasterService.MasterCandidate::compare 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; } // ElectMasterService::compareNodes private static int compareNodes(DiscoveryNode o1, DiscoveryNode o2) { // 集群状态版本相同时优先选择具有 master 资格的节点 if (o1.isMasterNode() && !o2.isMasterNode()) { return -1; } if (!o1.isMasterNode() && o2.isMasterNode()) { return 1; } // 以上条件都相同时选择 ID 较小的节点 return o1.getId().compareTo(o2.getId()); }
-
Шаг 9 выше проходит, когда есть активный мастер
ElectMasterService::tieBreakAcitveMasters
использоватьElectMasterService::compareNodes
Правила выбираются из activeMasters
Ожидает ли узел или голосует в соответствии с выбранным временным мастером
if (transportService.getLocalNode().equals(masterNode)) {
// 选出的临时 master 是本节点,则等待被选举为真正的 master
final int requiredJoins = Math.max(0, electMaster.minimumMasterNodes() - 1);
nodeJoinController.waitToBeElectedAsMaster(requiredJoins, masterElectionWaitForJoinsTimeout,
new NodeJoinController.ElectionCallback() {
@Override
public void onElectedAsMaster(ClusterState state) {
// 成功被选举为 master
synchronized (stateMutex) {
joinThreadControl.markThreadAsDone(currentThread);
}
}
@Override
public 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) {
joinThreadControl.stopRunningThreadAndRejoin("master_switched_while_finalizing_join");
}
joinThreadControl.markThreadAsDone(currentThread);
} else {
joinThreadControl.markThreadAsDoneAndStartNew(currentThread);
}
}
}
Выборы завершены
NodeJoinController::checkPendingJoinsAndElectIfNeeded
Сделайте узел мастером, когда у него будет достаточно голосов, и опубликуйте новое состояние кластера.
private synchronized void checkPendingJoinsAndElectIfNeeded() {
// 计算节点得票数
final int pendingMasterJoins = electionContext.getPendingMasterJoinsCount();
if (electionContext.isEnoughPendingJoins(pendingMasterJoins) == false) {
...
} else {
// 得票数足够,成为 master
electionContext.closeAndBecomeMaster();
}
}
public synchronized int getPendingMasterJoinsCount() {
int pendingMasterJoins = 0;
// 统计节点得票数,只计算拥有 master 资格节点的投票
for (DiscoveryNode node : joinRequestAccumulator.keySet()) {
if (node.isMasterNode()) {
pendingMasterJoins++;
}
}
return pendingMasterJoins;
}