Проектирование систем zk и etcd с точки зрения согласованности распределенных систем

распределенный

«При проектировании распределенных систем скептицизм, пессимизм и паранойя имеют смысл; на самом деле построение более надежных систем на менее надежном фундаменте — древняя идея в вычислительной технике».

ZooKeeperКак распределенная система координации приложений, она всегда была отраслевым эталоном в экосистеме Java с точки зрения управления конфигурацией, выбора главного узла, центра регистрации услуг и т. д. С момента своего открытого исходного кода она широко использовалась в архитектуре топовых открытых исходные системы в стране и за рубежом (Hadoop, Kafka, Dubbo), в то время какetcdКак восходящую звезду стека технологий golang, его часто сравнивают с zk по производительности, надежности, согласованности и т. д., а по некоторым аспектам и сценариям применения он превосходит zk;

Недавно я читал книгу «Проектирование систем с интенсивным использованием данных», и мне всегда хотелось учиться вместе со зрелыми распределенными системами и теориями с открытым исходным кодом.Кроме того, технологический стек компании раньше использовал zk, и я никогда не был знаком с etcd Воспользуйтесь этой возможностью, чтобы немного понять его сложную конструкцию системы;

Эта статья в основном разделена на две части:

  • 1. Ввести сравнение источника и структуры данных двух;
  • 2. Алгоритм распределенного консенсуса и анализ приложений в zk и etcd;

Примечание. Функции etcd, обсуждаемые в этой статье, основаны на версии v3;

происхождение имени

ZooKeeperОн был разработан и открыт исходным кодом Yahoo. Говорят, что источником названия является то, что в то время многие названия систем были названы в честь животных. В первоначальном видении дизайна zk должен был стать координирующим менеджером различных распределенных систем. , так что есть имя менеджера зоопарка;

а такжеetcdИмя состоит из двух смысловых частей (см. etcd Document), каталог «/etc» и «d» — системы распределения (распределенная система) unix-систем, каталог /etc отвечает за хранение данных конфигурации системы в unix-подобных системах, что можно увидетьetcdПо ожиданиям проектировщика, это распределенная база данных конфигурации с высокой стабильностью и согласованностью;

Модель данных ZooKeeper

ZooKeeperЛогическая структура данных представляет собой древовидную структуру, аналогичную файловой системе unix.В отличие от файловой системы, узел может быть либо файлом, либо каталогом, а его наименьшая единица данных называетсяZNode, каждыйZNodeДанные могут быть сохранены на узле, а дочерние узлы также могут быть смонтированы для формирования дерева;ZNodeКроме того, он делится на постоянные узлы и временные узлы.Постоянные узлы будут постоянно сохранены, если операция удаления не будет активно выполнена; в то время как временные узлы привязаны к сеансу клиента и автоматически удаляются при завершении сеанса; также разрешено назначать последовательность каждому узлу.Атрибуты для реализации упорядочения узлов;

Такие какDubboиспользоватьZooKeeperЧто касается структуры данных реестра, когда служба регистрации предоставляет поставщика узлов, она определяет, следует ли использовать постоянный узел в соответствии с динамической конфигурацией. механизм наблюдения используется для достижения динамического отказа службы; для служб Поскольку узлу-потребителю не нужно динамически изменяться, он может создать постоянный узел. Конкретная структура показана на следующем рисунке:


Кроме тогоZooKeeperВ али он не используется массово как служебный реестр.Коренная причина в том, чтоZooKeeperКак система CP, чтобы обеспечить согласованность данных, она жертвует определенной степенью доступности и не подходит в качестве крупномасштабного реестра услуг (подробный анализ будет сделан при обсуждении вопросов распределенной согласованности ниже).

Прикрепил:Почему Alibaba не использует ZooKeeper для обнаружения сервисов

модель данных etcd

etcdВерсия v2 является реализацией хранилища в памяти и не размещается на диске в режиме реального времени.Здесь мы обсуждаем только модель данных версии v3; etcd предназначена для надежного хранения редко обновляемых данных, а модель данных использует многоверсионную постоянную kv хранилище данных, ключ можно использовать любой При этом ключи сортируются в лексикографическом порядке при их сохранении, что удобно для диапазонных запросов по ключам, поэтому может быть реализована и иерархическая структура каталогов типа zk "/app" ;

etcdЛежащий в основе метод пары ключ-значение b+tree сохраняет физические данные (в настоящее время используется BoltDB), где ключ в kv представляет собой тройку (основной, подчиненный, тип):

  • major представляет основную версию ключа, major +1 за каждую фиксацию транзакции;
  • sub используется для различения разных ключей одной и той же основной ревизии, sub в одной и той же транзакции, +1 для каждой операции;
  • type используется для указания специального атрибута ключа, такого как type=t, что означает, что текущий узел был помечен захоронением, что означает, что он был сжат и удален; (etcd необходимо регулярно выполнять стратегию Compact, чтобы экономия места на диске)

Значение в kv содержит изменения предыдущей версии, например добавочные изменения предыдущей версии;

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

type keyIndex struct {
  key         []byte
  modified    revision // the main rev of the last modification
  generations []generation
}

// generation contains multiple revisions of a key.
type generation struct {
  ver     int64
  created revision // when the generation is created (put in first revision).
  revs    []revision
}

Объект генерации содержит исторический массив ревизий.Каждый раз, когда добавляется новая реверсия, новая версия добавляется в массив revs, что также реализуется etcd.MVCC(управление несколькими версиями), а индекс в памяти также имеет форму b+tree для ускорения запросов; ноKeyIndexСуществует также проблема: если ключ меняется много раз, массив Generation[0].revs становится очень большим. В настоящее время он должен полагаться на компактный механизм etcd для сжатия и активно отбрасывать некоторые исторические версии;

Ниже приведены комментарии к коду etcd.key_index.goОбработка жизненного цикла ключа описана в:

// For example: put(1.0);put(2.0);tombstone(3.0);put(4.0);tombstone(5.0) on key "foo"
// generate a keyIndex:
// key:     "foo"
// rev: 5
// generations:
//    {empty}
//    {4.0, 5.0(t)}
//    {1.0, 2.0, 3.0(t)}
//
// Compact a keyIndex removes the versions with smaller or equal to
// rev except the largest one. If the generation becomes empty
// during compaction, it will be removed. if all the generations get
// removed, the keyIndex should be removed.
//
// For example:
// compact(2) on the previous example
// generations:
//    {empty}
//    {4.0, 5.0(t)}
//    {2.0, 3.0(t)}
//
// compact(4)
// generations:
//    {empty}
//    {4.0, 5.0(t)}
//
// compact(5):
// generations:
//    {empty} -> key SHOULD be removed.
//
// compact(6):
// generations:
//    {empty} -> key SHOULD be removed.

Для выполненной операции захоронения, т.е. удаления, как только произойдет удаление, текущая генерация завершится, будет сгенерирована новая пустая генерация, а последняя операция реверсирования будет добавлена ​​с флагом type=t;

Резюме: Сопоставление между пользовательским ключом и keyIndex поддерживается в памяти b+tree, а keyIndex отвечает за ускорение запроса и поддержание отношения сопоставления ревизий, а пользовательское значение может быть извлечено из фактического хранилища БД через ревизию.

Алгоритмы консенсуса и консенсус

Вернемся к цитате из книги «Проектирование систем приложений с интенсивным использованием данных», приведенной в начале статьи:

«При проектировании распределенных систем скептицизм, пессимизм и паранойя имеют смысл; на самом деле построение более надежных систем на менее надежном фундаменте — древняя идея в вычислительной технике».

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

  • Частичные сбои внутри кластера (сбой ядра, сбой диска, перезапуск узла и т. д.)
  • Ненадежная сеть (потеря пакетов, сетевой раздел, перегрузка сети, тайм-аут сети и т. д.);
  • Ненадежные часы (синхронизация NTP опять-таки зависит от сети, локальные скачки времени и т. д.);

В типичной распределенной среде нет ни глобальных переменных, ни разделяемой памяти, а узлы даже не знают сейчас точное время, не говоря уже о других более сложных ситуациях. Поток информации может передаваться от одного узла к другому только через ненадежную сеть.Один узел не может принимать точные решения, но требует определенного консенсуса между несколькими узлами.Благодаря консенсусу все узлы могут согласиться с предложением.Для достижения консенсуса , это требует использования алгоритмов консенсуса, которые изучались и обобщались исследователями в области распределенных вычислений более десяти лет, среди них наиболее известные, такие какPaxos算法,Raft算法,ZabЖдать;

Алгоритм Паксос

Сначала краткое введениеPaxos算法,Paxos算法Есть три роли:

  • Предлагающий: отвечает за инициирование предложений (Предложение);
  • Акцептор: Ответственный за ответ и принятие предложения (Предложение), если предложение принято большинством, это означает, что предложение одобрено (Выбрано);
  • Учащийся: отвечает только за синхронизацию утвержденных (выбранных) предложений, не участвуя непосредственно в процессе утверждения предложений;

Предложение инициируется для избранного, что представляет собой двухэтапный процесс в алгоритме Paxos;

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

Среди них, для поведения Acceptor и Proposer, в алгоритме есть несколько ограничений для обеспечения точности алгоритма.Алгоритм Paxos славится своей сложностью, и трудно достичь идеальной инженерной практики.Поэтому он развивается на основе алгоритма Basic-Paxos.Подробности см. в алгоритме Paxos.); в дальнейшем мы в основном понимаем и анализируем протокол Zab (протокол атомарной широковещательной рассылки ZK) ZooKeeper и алгоритм raft с инженерной практикой в ​​etcd;

Заб протокол

Zab协议заимствованныйPaxos算法В этом есть некоторые идеи, но это не конкретная реализация paxos.Ее полное название Zookeeper Atomic Broadcast (протокол Zookeeper Atomic Broadcast).Zookeeper использует протокол Zab для построения основной и резервной системной архитектуры для обеспечения согласованности распределенная система данных;

Кратко опишите протокол Zab: используйте один процесс в кластере Zookeeper для обработки транзакционных операций, то есть узел-лидер, и конвертируйте транзакцию в Proposal через атомарный широковещательный протокол, который синхронизируется со всеми узлами-последователями;

Видно, что для обеспечения согласованности и порядка данных Zookeeper,只使用leader节点处理写请求, производительность его запроса на запись не способна расширяться по горизонтали.

Zab协议Есть два основных режима:

  • аварийное восстановление
  • широковещательное сообщение

1. Режим рассылки сообщений

  1. Лидер принимает запросы клиентов на транзакции и преобразует их в рассылку предложений;
  2. И присвоить глобально монотонно увеличивающийся уникальный идентификатор каждому предложению перед трансляцией, который также является идентификатором транзакции (ZXID) zk, и каждое предложение нужно сортировать и обрабатывать в порядке ZXID;
  3. Лидер выделит для каждого Последователя отдельную очередь, поставит Предложение в очередь и отправит сообщения по стратегии FIFO;
  4. После того, как Подписчик получает Предложение, он сначала записывает журнал транзакций на диск и отправляет Подтверждение Лидеру.Получив более половины Подтверждения, Лидер отправит сообщение фиксации на узел Последователя и завершит локальную отправку транзакции. .

2. Режим восстановления после сбоя

Когда кластер Zookeeper запущен или ведущий узел теряет связь с более чем половиной фолловеров по сетевым причинам, он входит в режим восстановления для повторного выбора мастера; протоколу Zab необходимо обеспечить эти характеристики при аварийном восстановлении:

  • Убедитесь, что транзакция (Committed), которая была зафиксирована на узле-лидере, окончательно зафиксирована всеми серверами;
  • Убедитесь, что отбрасывать нужно только транзакции, предложенные на узле-лидере;

Для достижения вышеперечисленных требований при разработке идентификатора транзакции (ZXID) протокола Zab ZXID представляет собой 64-битное число, а его младшие 32 бита представляют собой монотонно возрастающий простой счетчик Каждый раз, когда ведущий узел добавляет транзакция, она увеличивается на 1, а ее старшие 32 бита равны Число представляет собой значение эпохи цикла лидера.Каждый раз, когда избирается новый лидер, наибольший ZXID берется из его локального журнала транзакций, а старшие 32 бита добавляется 1, которая используется в качестве новой эпохи, а младшие 32 бита изменяются с 0 перезапуска, генерируя новый ZXID.

Протокол Zab упрощает процесс ресинхронизации данных при аварийном восстановлении Leader через этот механизм, в крайних случаях может потребоваться некоторое время для восстановления данных и перевыбора синхронизации данных большого количества узлов кластера, а сервис zk недоступен во время процесс переизбрания.Да, это также определенная жертва доступности, сделанная в протоколе Zab для обеспечения строгой согласованности данных;

Из приведенного выше понимания различных сценариев протокола Zab видно, что целью проектирования системы zk является высокосогласованная распределенная система, которая удовлетворяет условиям CP в теории CAP.Существуют такие проблемы, как нерасширяемая производительность записи и медленное восстановление после сбоя. :

  1. Структура системы CP не может удовлетворить требования реестра к высокой доступности, и еще менее вероятно, что реестр не будет иметь доступ к службам;
  2. Производительность записи нельзя масштабировать по горизонтали, после роста масштаба сервиса каждый перезапуск сервиса может стать катастрофой;
  3. обнаружение активности zk, основанное на сеансе, не является полностью надежным, и поставщик услуг должен активно возвращать информацию об обнаружении работоспособности;
  4. Учитывая катастрофоустойчивость, даже если реестр полностью отключен, это не может повлиять на вызов службы, необходимо активно реализовать механизм кэширования службы через клиент, что не поддерживается нативным клиентом zk;
  5. Как более старый член семейства распределенных систем, zk не может получать своевременную поддержку и часто требует, чтобы пользователи zk руководили и поддерживали;

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

Алгоритм плота

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

1. Выборы лидера

Члены кластера Raft имеют три состояния:

  1. Лидер, отвечающий за обработку всех транзакционных операций и синхронизацию с другими узлами-репликами;
  2. Подчиненный, получает данные синхронизации от Ведущего, записывает локальный журнал, каждый Подчиненный поддерживает пульсацию с Ведущим;
  3. Condidate, промежуточное состояние лидера и ведомого, означает, что после сбоя ведущего и потери отклика на сердцебиение ведомый превращается в ведомого и инициирует новый раунд выборов лидера до тех пор, пока не будет сгенерирован новый лидер.

Протокол Raft поддерживает уникальный термин в кластере, то есть срок лидера.term_id будет увеличиваться каждый раз, когда инициируется новый раунд выборов.При инициализации все члены кластера являются последователями, и все последователи могут участвовать во время старта выборов.В выборах фолловер добавляет локальный максимум term_id+1 и инициирует выборное голосование.После этого статус меняется на Condidate и ждет пока проголосуют другие серверы.Только когда более половины голоса получены, последователь может стать лидером.Учитывая византийскую общую проблему, теоретически все узлы являются доверенными в среде кластера);

Что делать в нестандартных сценариях, если ни один кандидат не получил более половины голосов? В протоколе Raft есть два тайм-аута для обработки этих сценариев исключений:

  • election timeout
    Последователь ожидает, чтобы стать Condidate.Этот тайм-аут представляет собой случайное время между 150 ~ 300 мс, что гарантирует, что несколько узлов не станут Condidate и не инициируют голосование одновременно; в то же время каждый узел будет голосовать только в первый раз, когда узел голосует. После голосования сбросить тайм-аут выборов, чтобы гарантировать, что выборы могут быть повторно инициированы, когда сообщение об успешном выборе лидера не получено;

  • heartbeat timeout
    После успешного избрания лидера между ведомым и лидером сохраняется период тайм-аута пульса, когда ведомый теряет пульс, он перезапускается, ожидая, чтобы стать кондидатом, и после ожидания инициирует избирательное голосование;

Во-вторых, синхронизация журналов (репликация журналов)

После избрания лидера начинается синхронизация данных, то есть синхронизация журналов;和 Zab 一样,Raft 中也是主节点负责接收所有客户端的处理请求; Конкретный процесс одноразовой синхронизации данных:

  1. После того, как лидер получает запрос на транзакцию, он попадает в локальный журнал, обычно в журнал стены, и помечает его как незафиксированный, и отправляет запрос на синхронизацию данных всем подписчикам. используйте сообщение Append Entries для синхронизации;
  2. После того, как ведомый получит сообщение Append Entries, он должен вернуть ACK ведущему;
  3. После того, как Лидер получит более половины ACK, он зафиксирует транзакцию и вернет успех клиента, и в то же время сделает фиксацию Последователя;

В алгоритме Raft необходимо обеспечить, чтобы все зафиксированные журналы применялись ко всем узлам.Если Подписчик теряет транзакцию из-за проблемы в сети или получает транзакцию без отправки ACK,这里和 Zab 思路也是基本一致, то есть ведущий узел поддерживает индекс журнала, который записывает текущую обработку последователя для каждого последователя, и каждый раз, когда отправляются Append Entries, он переносит термин, индекс и данные данных (это имеет место в журнале wal для etcd) , Если исключение достигнуто, добавление записей будет повторно отправлено до тех пор, пока оно не будет успешным.

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

сравнение сцен

Наконец, обратитесь к сравнению, приведенному в официальной документации etcd.etcd why, чтобы сравнить и объяснить etcd и Zookeeper с точки зрения сценариев использования, функций приложений и т. д.

Две системы, etcd и Zookeeper, решают одну и ту же проблему, а именно: распределенную координацию системы и хранение метаданных, но etcd, как опоздавший, может стоять на плечах предшественников Zookeeper, и является более перспективным и полностью используемым в дизайн системы.Ссылаясь на дизайн и реализацию zk, есть много ссылок в практике консенсусного протокола и проектирования структуры данных.Во время обучения etcd также реализует некоторые возможности за пределами zk на основе zk:

  • Динамическая настройка топологии кластера (динамическое увеличение емкости, уменьшение емкости)
  • Более стабильные возможности чтения и записи при высокой нагрузке
  • Модель данных управления параллелизмом с несколькими версиями MVCC
  • Надежный мониторинг событий и воспроизведение в зависимости от версии на основе модели данных MVCC.
  • Механизм аренды отделяет соединение от сеанса
  • Более безопасные вызовы API

В то же время, etcd лучше, чем Zookeeper, с точки зрения поддержки многоязычных вызовов.Zookeeper в основном ограничен экосистемой Java из-за своего механизма реализации.etcd предоставляет очень удобный API для многоязычных вызовов и простую команду curl может вызвать его Http-сервис.

Таким образом, при разработке архитектуры системы предпочтение отдается etcd, учитывая более высокую производительность, больше функций и более своевременную поддержку сообщества.В то же время etcd также может поддерживать некоторые старые системы, использующие API-интерфейс Zookeeper. возможности, см. подробностиzetcd.

Кроме того, для сценария обнаружения службы RPC существуют системы, более ориентированные на этот сегмент, такие как Consul, Eureka, Nacos и т. д. Для рассмотрения долгосрочного планирования архитектуры и конкретных сценариев приложений эти программные системы с открытым исходным кодом больше подходят, чем etcd и Zookeeper.Как компонент обнаружения служб в распределенной архитектуре удаленного вызова, он обладает передовым опытом в сценариях обнаружения служб большего масштаба.


исходный адрес

Список цитирования:

  1. Документация официального сайта ZooKeeper
  2. От Паксос до зоопарка
  3. Документация реестра Dubbo zk
  4. etcd архитектура и анализ реализации
  5. Анализ принципа проектирования etcd
  6. Анализ принципа etcd v3
  7. Подробное объяснение протокола Raft