Реализация протокола Raft: Часть 0 — Введение

Go

Переведено сEli BenderskyизСерия блогов, уполномоченный оригинальным автором.

Эта статья является предисловием к серии статей, предназначенных для ознакомления с протоколом распределенного консенсуса Raft и его реализацией в Go. Полный список статей ниже:

Raft— относительно новый алгоритм (2014 г.), но уже широко используемый в отрасли. Пожалуй, самый известный случайKubernetes, где компонент распределенного хранилища ключей и значенийetcdОн основан на протоколе Raft.

Цель этой серии статей — описать полностью функциональную и тщательно протестированную версию протокола Raft.Метод реализацииИ обеспечивают некоторое интуитивное понимание того, как работает плот.Это не единственный способ изучить протокол Raft.. Я полагаю, вы хотя бы читалиРафт Бумаги; Кроме того, настоятельно рекомендуется уделить время тщательному изучениюTwitterFacebookВеб-сайт RaftРесурсы: посмотрите один или два выступления создателя, поэкспериментируйте с инструментами визуализации алгоритма, просмотрите докторскую диссертацию Онгаро, чтобы узнать больше подробностей, и многое другое.

Не ожидайте, что полностью освоите протокол Raft за один день. Хотя Raft разработан так, чтобы его было легче понять, чем Paxos, алгоритм Raft все еще довольно сложен. Проблема, которую он пытается решить (распределенная согласованность), является сложной проблемой, поэтому решение, естественно, не слишком простое.

копировать конечный автомат

Алгоритмы распределенного консенсуса можно рассматривать как решение проблемы репликации детерминированного конечного автомата на серверах. здесьГосударственный аппаратТермин может использоваться для обозначения любой услуги. В конце концов, конечные автоматы — это одна из основ информатики, и все может быть представлено конечными автоматами. Базы данных, файловые серверы, серверы блокировки и т. д. можно рассматривать как сложные конечные автоматы.

Рассмотрим конечный автомат для представления некоторой службы, к которой могут подключаться несколько клиентов, делать запросы и ожидать ответов:

Single state machine with two clients

До тех пор, пока сервер работает на станке стабильный и надежный, эта система может работать нормально. Если сервер сбои, наши услуги недоступны, и это недопустимо. Как правило, наша надежность системы зависит от его запущенного сервера.

Один общий метод - улучшить надежность обслуживаниякопировать. Мы можем запускать несколько экземпляров службы на разных серверах. Это создает серверкластер, эти серверы работают вместе для предоставления услуг, и сбой любого из них не приведет к прерыванию обслуживания. Сервера изолированы друг от друга[1]Некоторые распространенные сбои, затрагивающие несколько серверов одновременно, могут быть устранены, что еще больше повысит надежность системы.

Клиенты больше не подключаются к одной обслуживающей машине, а ко всему кластеру. Кроме того, реплики служб, составляющих кластер, должны взаимодействовать друг с другом, чтобы правильно реплицировать состояние:

Replicated state machine with two clients

Каждый конечный автомат на приведенной выше диаграмме является репликой службы. Идея состоит в том, что все конечные автоматы работают синхронно, принимают одни и те же входные данные от клиентских запросов и выполняют одни и те же переходы между состояниями. Это гарантирует, что даже в случае сбоя некоторых серверов в кластере клиенту будет возвращен тот же результат. Raft — это алгоритм для этой цели.

Теперь поясним некоторые термины, которые будут часто использоваться в следующем тексте:

  • Оказание услуг: это логическая задача в распределенной системе, которую мы будем реализовывать, скажем, базу данных ключ-значение.
  • Сервер (Сервер)илиКопия (Реплика): экземпляр службы Raft, работающий на изолированной машине, которая может подключаться к другим репликам или клиентам по сети.
  • Кластер (кластер): Набор серверов Raft, которые взаимодействуют для реализации распределенных сервисов, типичный размер кластера — 3 или 5.

Модули согласованности и журналы Raft

Теперь давайте взглянем на один из конечных автоматов, показанных на диаграмме выше. Рафту, как общему алгоритму, все равно, как сервис реализован по конечному автомату. Его цель — надежно и точно зафиксировать и воспроизвести то, что принимает конечный автомат.войтиПоследовательность (также вызываемая терминологией Raftинструкция), учитывая начальное состояние и все входные данные, конечный автомат можно воспроизвести с полной точностью. Это можно понять и с другой точки зрения: если у нас есть две независимые копии одного и того же конечного автомата и мы выдаем им одну и ту же последовательность входных данных, начиная с одного и того же начального состояния, то обе копии окажутся в одном и том же состоянии и будут производить одно и то же. Выход.

Вот структура общего сервиса с использованием Raft:

Raft consensus module and log connected to state machine

Подробное описание этих компонентов выглядит следующим образом:

  • Государственная машина такая же, как мы говорили ранее. Он означает любую услугу: типичным примером при представлении Raft является хранилище ключей и значений.
  • Журнал (Log) — это место, где хранятся все инструкции (вводы), выданные клиентом. Эти инструкции не применяются непосредственно к конечному автомату, вместо этого алгоритм Raft фиксирует эти инструкции только в том случае, если они были успешно реплицированы на большинство серверов. также. Журнал является постоянным - он хранится в стабильном хранилище, устойчивом к сбоям системы и может использоваться после сбоя системы.воспроизвестиГосударственный аппарат.
  • Модуль согласованностиЭто ядро ​​алгоритма Raft. Он принимает команды от клиентов, обеспечивает их сохранение в журнале, реплицирует команды на другие реплики Raft в кластере (зеленые стрелки на изображении выше) и фиксирует их в конечном автомате, когда это безопасно. Отправка в конечный автомат уведомляет клиента о фактической модификации.

Лидеры и последователи

Плот используетсильное лидерствоМодель, в которой копия в кластере является лидером, а за ней следуют другие копии. Лидер отвечает за прием запросов клиентов, копирование инструкций для последователей и возврат клиенту.

При нормальной работе цель ведомого — просто копировать журнал ведущего. В случае выхода из строя ведущего или разделения сети его берет на себя ведомый, поэтому услуги по-прежнему доступны.

В этой модели есть плюсы и минусы. Важным преимуществом является простота, данные всегда идут от лидера к последователям, и только лидер отвечает на запросы клиентов. Такой дизайн упрощает анализ, тестирование и отладку протокола Raft. Недостатком является производительность — так как в кластере только один сервер взаимодействует с клиентами, это может стать узким местом в системе при пике клиентских запросов. Ответ на этот вопрос обычно таков: протокол Raft не подходит для сервисов с высоким трафиком. Протокол Raft больше подходит для сервисов с низким трафиком, которые гарантируют согласованность за счет доступности — мы вернемся к этому в разделе «Отказоустойчивость».

Взаимодействие с клиентами

Я писал ранее, "Клиент больше не будет подключаться к одной машине, предоставляющей услуги, а будет подключаться ко всему кластеру", что это значит? Кластер — это просто группа серверов, соединенных сетью, так как же соединить «весь кластер»?

Ответ прост:

  • При доступе к кластеру Raft клиент знает сетевой адрес копии кластера. Что касается того, как он узнает (например, через механизм обнаружения служб), выходит за рамки этой статьи.
  • Клиент вначале отправит запрос на любую реплику.Если реплика является ведущей, то она сразу примет запрос, а клиент также будет ждать полного ответа.После этого клиент запомнит, что реплика является лидера, и в будущем в этом не будет необходимости.Ищите лидера снова (если не произойдет какой-либо сбой, например, крах лидера).
  • Если реплика сообщает, что она не является лидером, клиент попытается подключиться к другой реплике. Здесь можно провести оптимизацию, когда реплики-последователи напрямую сообщают клиенту, какая реплика является ведущей. Поскольку реплики всегда взаимодействуют друг с другом, правильный ответ обычно известен, что экономит время клиента на угадывание.
  • Также возможен случай, когда клиент поймет, что он не подключен к лидеру, то есть его запрос не был успешно отправлен в течение периода ожидания. Это может означать, что реплика, к которой он подключен, на самом деле не является лидером (даже если она так думает) — она может быть разделена с другими серверами Raft. По истечении тайм-аута клиент снова будет искать других лидеров.

Оптимизации, упомянутые в пункте 3, в большинстве случаев не нужны. В общем, полезно различать «нормальную работу» и «исключительные условия» в среде Raft. Служба обычно «запущена и работает» 99,9% времени, и в этот момент клиенты узнают, кто является лидером, потому что они кэшируют эту информацию при первом подключении к службе. В сценариях отказа определенно есть путаница (подробнее об этом в следующем разделе), но только на короткое время. Как мы подробно расскажем в следующей статье, кластеры Raft могут очень быстро восстанавливаться после временных сбоев компьютеров или проблем с сетевыми разделами — в большинстве случаев в течение секунды. Может быть кратковременное состояние недоступности, когда новый лидер заявляет о своем лидерстве, а клиенты ищут конкретную реплику лидера, но затем кластер возвращается к «нормальной работе».

Механизм отказоустойчивости Raft и теория CAP

Давайте посмотрим на схему трех реплик Raft, на этот раз без подключения клиентов:

Replicated state machine not showing clients

Какие типы отказов мы можем предвидеть в этом кластере?

Каждый компонент современного компьютера может выйти из строя, но для обсуждения давайте рассмотрим сервер, работающий в экземпляре Raft, как атомарную единицу. В этом случае мы сталкиваемся с двумя основными категориями отказов:

  1. Произошел сбой серверов, и один из серверов перестал отвечать на все сетевые запросы в течение определенного периода времени. Вышедшие из строя серверы обычно перезапускаются и снова включаются в работу после кратковременного простоя.
  2. Сетевой раздел, в котором один или несколько серверов отключены от других серверов и/или клиентов из-за проблем с сетевым оборудованием или средой передачи.

С точки зрения сервера А он взаимодействует с сервером Б, и сбой сервера Б неотличим от сетевого раздела между А и Б. Поведение этих двух случаев одинаковое — A не получает никакой информации и ответа от B. Однако с точки зрения системы сетевые разделы более эффективны, поскольку они влияют на несколько серверов одновременно. В следующей части этой серии мы обсудим некоторые сложные сценарии, вызванные сетевыми разделами.

Чтобы изящно обрабатывать произвольные сетевые разделы и сбои сервера, Raft требуетнаиболееСервер обычно начинается и доступен лидеру в любой момент времени. RAFT может терпеть 1 сбой машины, если есть 3 сервера, и 2 сбои машин для кластера 5 серверов; для2N+1сервер, который может позволитьNсервер вышел из строя.

Это ведет ктеория CAP, практический вывод состоит в том, что когда есть сетевые разделы (неизбежная часть практических приложений), мы должны тщательно взвеситьДоступностьа такжепоследовательность.

В этом компромиссе Raft твердо стоит на своемпоследовательностьлагерь. Он предназначен для предотвращения ситуаций, когда кластер может прийти в несогласованное состояние, когда разные клиенты могут получать разные ответы. Raft жертвует некоторым удобством использования ради этого.

Как я вкратце упоминал ранее, Raft не предназначен для высокопроизводительных мелкомодульных сервисов. клиентаКаждыйКаждый запрос запускает цепочку работы — связь между репликами Raft для репликации инструкций для большинства сервисов и их сохранения; это происходит до того, как клиент получит ответ.

Например, вы точно не стали бы проектировать реплицированную базу данных, в которой все клиентские запросы проходят через Raft, это было бы слишком медленно. Raft лучше подходит для крупномасштабных распределенных примитивов, таких как внедрение серверов блокировки, выбор лидеров для протоколов более высокого уровня, репликация критически важных данных конфигурации в распределенных системах и т. д.

Почему стоит выбрать Go

Реализация Raft, представленная в этой серии, написана на Go. На мой взгляд, язык Go имеет три основных преимущества, поэтому в этой серии и в общих веб-сервисах Go выбран в качестве языка реализации:

  1. Параллелизм: Алгоритмы, такие как Raft, полностью параллельны по своей природе, каждая реплика выполняет непрерывные операции (инструкции), запускает таймеры для синхронизированных событий и должна отвечать на запросы от других реплик и клиентов. я писал раньшеПочему я считаю Go идеальным языком для написания такого кода.
  2. Стандартная библиотека: Go Language имеет мощную стандартную библиотеку промышленной классы, которая позволяет легко написать сложные веб-серверы без импорта и изучения любых сторонних библиотек. В частности, в сокровении первого вопроса, который необходимо столкнуться, - это «Как сообщения случаются между репликами?», И многие люди увязывают в проектировании протоколов и сериализации, или используют тяжелые сторонние библиотеки. Иди язык имеетnet/rpc, что является достаточным решением для такого рода задач, может быть использовано быстро и не требует представления (зависимости).
  3. Просто: достижение распределенной согласованности достаточно сложно, даже независимо от языка программирования. Ясный, простой код можно написать на любом языке, но это идиома по умолчанию в Go, языке, который противостоит сложности кода на всех возможных уровнях.

Следующий шаг

Спасибо, что дочитали до этого места! Дайте мне знать, если вы думаете, что я мог бы написать лучше. Хотя концепция Raft может показаться простой, как только мы напишем реализацию, мы столкнемся с множеством проблем. Последующие части этой серии будут посвящены более подробной информации о различных аспектах алгоритма Raft.

Теперь вы должны быть готовы войтичасть 1, приступим к реализации Raft.

Примечание переводчика

Используя Golang для реализации протокола Raft, эта серия статей не только интуитивно объясняет некоторые трудности в протоколе Raft, но и очень помогает в изучении параллельного программирования на языке Go.

После прочтения оригинального блога я чувствую, что получил много пользы. После получения согласия автора я перевел эту серию блогов на китайский язык и поделился ими со всеми. Я надеюсь, что студенты, интересующиеся Go или Raft, смогут что-то получить.

Настоятельно рекомендуется, чтобы читатели после прочтения статьи могли сделатькод автораЗакрепите свое понимание протокола Raft на основе журнала выходных данных теста.

Нахожусь в процессе обучения, форкаю оригинальный авторский код, добавляю китайские комментарии на основе оригинала, добавляю вывод тестовых случаев. Для читателей не удобно выполнять тест, где можно просмотреть вывод журнала теста напрямую.

Кому надо забрать, адрес Github:GitHub.com/GU или элемент A/…


  1. Например, их можно разместить в разных стойках, или подключить к разным источникам питания, или даже разместить в разных зданиях. Действительно важные услуги, предоставляемые крупными компаниями, обычно находятся вглобальноТиражированные копии будут распространяться в разных регионах.↩︎