«При проектировании распределенных систем скептицизм, пессимизм и паранойя имеют смысл; на самом деле построение более надежных систем на менее надежном фундаменте — древняя идея в вычислительной технике».
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;
- На этапе подготовки предложение инициируется предлагающим.Предложение включает глобально уникальный идентификатор предложения и значение содержания предложения, а полученное предложение утверждается принимающей стороной.
- Предложение, одобренное большинством на этапе Принятия, формирует резолюцию и синхронизируется Учащимся;
Среди них, для поведения 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. Режим рассылки сообщений
- Лидер принимает запросы клиентов на транзакции и преобразует их в рассылку предложений;
- И присвоить глобально монотонно увеличивающийся уникальный идентификатор каждому предложению перед трансляцией, который также является идентификатором транзакции (ZXID) zk, и каждое предложение нужно сортировать и обрабатывать в порядке ZXID;
- Лидер выделит для каждого Последователя отдельную очередь, поставит Предложение в очередь и отправит сообщения по стратегии FIFO;
- После того, как Подписчик получает Предложение, он сначала записывает журнал транзакций на диск и отправляет Подтверждение Лидеру.Получив более половины Подтверждения, Лидер отправит сообщение фиксации на узел Последователя и завершит локальную отправку транзакции. .
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.Существуют такие проблемы, как нерасширяемая производительность записи и медленное восстановление после сбоя. :
- Структура системы CP не может удовлетворить требования реестра к высокой доступности, и еще менее вероятно, что реестр не будет иметь доступ к службам;
- Производительность записи нельзя масштабировать по горизонтали, после роста масштаба сервиса каждый перезапуск сервиса может стать катастрофой;
- обнаружение активности zk, основанное на сеансе, не является полностью надежным, и поставщик услуг должен активно возвращать информацию об обнаружении работоспособности;
- Учитывая катастрофоустойчивость, даже если реестр полностью отключен, это не может повлиять на вызов службы, необходимо активно реализовать механизм кэширования службы через клиент, что не поддерживается нативным клиентом zk;
- Как более старый член семейства распределенных систем, zk не может получать своевременную поддержку и часто требует, чтобы пользователи zk руководили и поддерживали;
Для сценария, в котором zk используется в качестве центра регистрации, упомянутого в предыдущей статье, эти проблемы являются фатальными проблемами после расширения масштаба на более позднем этапе, поэтому также важно разумно рассматривать сценарии использования zk в проекта (таких как офлайн-обработка больших данных, хранение метаданных и т. д.), zk по-прежнему остается мощным инструментом в распределенной архитектуре.
Алгоритм плота
По сравнению с алгоритмом Paxos алгоритм Raft снижает сложность протокола, облегчая инженерам понимание и выполнение инженерных практик; в алгоритме Raft проблема алгоритма консенсуса делится на две подзадачи: выбор лидера и синхронизация журналов. ;
1. Выборы лидера
Члены кластера Raft имеют три состояния:
- Лидер, отвечающий за обработку всех транзакционных операций и синхронизацию с другими узлами-репликами;
- Подчиненный, получает данные синхронизации от Ведущего, записывает локальный журнал, каждый Подчиненный поддерживает пульсацию с Ведущим;
- Condidate, промежуточное состояние лидера и ведомого, означает, что после сбоя ведущего и потери отклика на сердцебиение ведомый превращается в ведомого и инициирует новый раунд выборов лидера до тех пор, пока не будет сгенерирован новый лидер.
Протокол Raft поддерживает уникальный термин в кластере, то есть срок лидера.term_id будет увеличиваться каждый раз, когда инициируется новый раунд выборов.При инициализации все члены кластера являются последователями, и все последователи могут участвовать во время старта выборов.В выборах фолловер добавляет локальный максимум term_id+1 и инициирует выборное голосование.После этого статус меняется на Condidate и ждет пока проголосуют другие серверы.Только когда более половины голоса получены, последователь может стать лидером.Учитывая византийскую общую проблему, теоретически все узлы являются доверенными в среде кластера);
Что делать в нестандартных сценариях, если ни один кандидат не получил более половины голосов? В протоколе Raft есть два тайм-аута для обработки этих сценариев исключений:
-
election timeout
Последователь ожидает, чтобы стать Condidate.Этот тайм-аут представляет собой случайное время между 150 ~ 300 мс, что гарантирует, что несколько узлов не станут Condidate и не инициируют голосование одновременно; в то же время каждый узел будет голосовать только в первый раз, когда узел голосует. После голосования сбросить тайм-аут выборов, чтобы гарантировать, что выборы могут быть повторно инициированы, когда сообщение об успешном выборе лидера не получено; -
heartbeat timeout
После успешного избрания лидера между ведомым и лидером сохраняется период тайм-аута пульса, когда ведомый теряет пульс, он перезапускается, ожидая, чтобы стать кондидатом, и после ожидания инициирует избирательное голосование;
Во-вторых, синхронизация журналов (репликация журналов)
После избрания лидера начинается синхронизация данных, то есть синхронизация журналов;和 Zab 一样,Raft 中也是主节点负责接收所有客户端的处理请求
; Конкретный процесс одноразовой синхронизации данных:
- После того, как лидер получает запрос на транзакцию, он попадает в локальный журнал, обычно в журнал стены, и помечает его как незафиксированный, и отправляет запрос на синхронизацию данных всем подписчикам. используйте сообщение Append Entries для синхронизации;
- После того, как ведомый получит сообщение Append Entries, он должен вернуть ACK ведущему;
- После того, как Лидер получит более половины 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.Как компонент обнаружения служб в распределенной архитектуре удаленного вызова, он обладает передовым опытом в сценариях обнаружения служб большего масштаба.
Список цитирования: