Автор Билгин Ибрям Оригинал Здесь .
Введение переводчика Я хотел освежить свои знания и взглянуть на новые подходы к реализации распределенных транзакций и наткнулся, смею сказать, на монументальную статью на актуальную тему.
Я решил, что эту информацию следует донести до максимального числа заинтересованных лиц, поэтому публикую перевод здесь.
Текст насыщен терминами, которые трудно перевести на русский язык и которые обычно используются как есть.
Сначала была идея оставить английские названия, но в результате текст становится на 30% английским, поэтому в итоге я попробовал перевести все термины.
Если у вас есть замечания по переводу отдельных терминов, пишите в личное сообщение.
Наслаждайся чтением! Будучи архитектором-консультантом в Red Hat, я имел возможность работать над многими проектами для наших клиентов.
Каждый из них имеет свои особенности, но имеет и некоторые общие черты.
Большинство клиентов хотят знать, как координировать запись в несколько систем одновременно.
Ответ на этот вопрос обычно включает подробное объяснение двойной записи, распределенных транзакций, современных альтернатив, а также возможных сценариев сбоя и недостатков каждого подхода.
Как правило, именно в этот момент заказчик понимает, что разделение монолитного приложения на микросервисы - долгий и трудный путь, обычно требующий компромиссов.
Вместо того, чтобы углубляться в подробное обсуждение транзакций, в этой статье кратко описываются основные подходы и шаблоны координации записи в несколько ресурсов.
Я знаю, что у вас может быть хороший или плохой прошлый опыт использования одного или нескольких из этих подходов, но на практике, в правильном контексте и с правильными ограничениями, все эти методы работают довольно хорошо.
Обычно руководители групп несут ответственность за выбор наилучшего подхода в зависимости от контекста.
Примечание : Если вас интересует двойная запись, ознакомьтесь с моей сессией Red Hat Summit 2021, где я подробно рассказал.
ты тоже можешь просмотреть слайды из моей презентации.
В настоящее время я работаю с Red Hat OpenShift Streams для Apache Kafka. полностью управляемый сервис Apache Kafka .
Начало работы занимает меньше минуты, а в течение пробного периода оно совершенно бесплатно.
Попробуй и помогите нам сформировать его с помощью ваших первых отзывов.
Если у вас есть вопросы или комментарии по поводу этой статьи, напишите мне в Twitter. @bibryam , и начнем.
Проблема двойной записи
Единственный индикатор того, что у вас возникла проблема с двойной записью, — это необходимость управлять записью в более чем одну систему.Это требование может быть неочевидным и выражаться по-разному при проектировании распределенных систем.
Например:
- Вы выбрали лучшие инструменты для каждой задачи, и теперь вам нужно обновить базу данных NoSQL, поисковый индекс и кэш в рамках одной бизнес-транзакции;
- Созданная вами служба должна обновить свою базу данных, а также отправить уведомление об изменении другой службе;
- У вас есть бизнес-операции, которые влияют на ряд других услуг;
- Возможно, вам придется реализовать идемпотентность для службы, поскольку конечным пользователям придется повторять неудачные вызовы.
Наш скрипт — это клиентское приложение, которое вызывает микросервис для внесения изменений.
Служба A (Служба A на рисунках) должна обновить свою базу данных A (База данных A), но она также должна вызвать службу B (Служба B) для операции записи, как показано на рисунке 1. Фактический тип базы данных, а также протокол связи между сервисами не имеет отношения к нашему обсуждению, поскольку проблема остается той же.
Рисунок 1. Проблема двойной записи в микросервисах.
Небольшое, но важное пояснение ниже объясняет, почему не существует простых решений этой проблемы.
Если сервис A записывает данные в свою базу данных, а затем отправляет уведомление в очередь для сервиса B (назовем это подходом «локальная фиксация, затем публикация») , все еще существует вероятность того, что приложение не будет работать надежно.
Пока Служба A записывает в свою базу данных, а затем отправляет сообщение в очередь, существует небольшая вероятность того, что приложение выйдет из строя после сохранения в базу данных и перед второй операцией, оставив систему в несогласованном состоянии.
Если сообщение отправляется до записи в базу данных (назовем этот подход «опубликовать, затем локальный коммит» ), существует вероятность сбоя записи в базу данных или проблемы с синхронизацией, из-за которой служба B получит событие до того, как служба A зафиксирует изменение в своей базе данных.
В любом случае этот сценарий предполагает двойную запись в базу данных и в очередь, и это основная проблема, которую мы собираемся исследовать.
В следующих разделах я расскажу о различных подходах, доступных сегодня для решения этой насущной проблемы.
Модульный монолит
Проектирование вашего приложения в виде модульного монолита может показаться хаком или шагом назад в эволюции архитектуры, но на практике это работает хорошо.Это не лучшие практики создания микросервисов, а скорее исключения из правил, которые можно осторожно комбинировать с микросервисами, когда строгая согласованность записи является основным требованием, даже более важным, чем возможность независимого развертывания и масштабирования микросервисов.
Монолитная архитектура не означает, что система плохо спроектирована или плоха, и ничего не говорит о качестве.
Как следует из названия, это система, спроектированная по модульному принципу с одним модулем развертывания.
Обратите внимание, что это специально разработанная и реализованная модульный монолит , который отличается от случайно созданного монолита, который растет со временем.
В специально созданной модульной монолитной архитектуре все модули следуют принципам микросервисов, при этом каждый модуль инкапсулирует весь доступ к своим данным, но операции выполняются в рамках общего процесса.
Архитектура модульного монолита
При таком подходе вам необходимо преобразовать микросервисы (Сервис A и Сервис B) в библиотечные модули, которые можно будет развернуть в общей среде выполнения.Затем вы настраиваете обе микрослужбы для использования одной и той же базы данных.
Поскольку службы пишутся и развертываются как библиотеки в общей среде выполнения, они могут использовать один и тот же механизм транзакций.
Поскольку модули совместно используют экземпляр базы данных, вы можете использовать локальную транзакцию для одновременного подтверждения или отката всех изменений.
Нам также необходимо развертывать эти модули по-другому, поскольку мы хотим, чтобы модули развертывались как библиотеки в более крупном развертывании и использовали общий механизм локальных транзакций.
Даже в монолитной архитектуре есть способы изолировать код и данные.
Например, вы можете разделить модули на отдельные пакеты, собрать модули и репозитории исходного кода, которые могут принадлежать разным командам.
Вы можете выполнить частичную изоляцию данных, сгруппировав таблицы по соглашению об именах, схеме, экземпляру базы данных или даже по серверу базы данных.
Диаграмма на рис.
2 вдохновлена отчетом Акселя Фонтена о величественные модульные монолиты , иллюстрирует различные уровни изоляции кода и данных в приложениях.
Рисунок 2. Уровни изоляции кода и данных для приложений.
Последняя часть головоломки — использовать единую среду выполнения и службу-оболочку, которая может использовать другие модули и включать их в контекст существующей транзакции.
Все эти особенности реализации делают модули более тесно связанными, чем типичные микросервисы, но преимущество состоит в том, что служба-оболочка может запускать транзакцию, вызывать библиотечные модули для обновления своих баз данных, а также фиксировать или откатывать транзакцию как одну операцию, не беспокоясь о частичных ошибках.
сбой или возможное несоответствие.
В нашем примере, показанном на рисунке 3, мы преобразовали Службу A и Службу B в библиотеки и развернули их в общей среде выполнения, либо одна из служб могла выступать в качестве общей среды выполнения.
Таблицы базы данных также используют один экземпляр базы данных, разделенный на группы таблиц, управляемые соответствующими библиотечными службами.
Рисунок 3. Модульный монолит с общей базой данных.
Преимущества и недостатки модульного монолита
Оказывается, в некоторых отраслях преимущества такой архитектуры гораздо важнее, чем более быстрая доставка и скорость изменений, которые так высоко ценятся в других местах.В таблице 1 обобщены преимущества и недостатки модульной монолитной архитектуры.
Преимущества | Простая семантика транзакций с локальными транзакциями, обеспечивающими согласованность данных, чтение/запись, откат и т. д. |
Недостатки | Общая среда выполнения не позволяет нам самостоятельно развертывать и масштабировать модули или изолировать сбои одного модуля.
Логическое разделение таблиц в одной базе данных не является прочным. Со временем база данных может стать общим слоем интеграции. Связывание модулей и совместное использование контекста транзакции требуют координации на этапе проектирования и увеличивают связь между сервисами.
|
Примеры | Среды выполнения, такие как Apache Karaf и WildFly, которые обеспечивают модульное и динамическое развертывание сервисов.
Компоненты Apache Camel Direct и Direct-VM позволяют открывать операции для вызовов памяти и поддерживать контексты транзакций в процессе JVM. Apache Isis — один из лучших примеров модульной монолитной архитектуры. Он позволяет разрабатывать доменно-ориентированные приложения путем автоматического создания API-интерфейсов пользовательского интерфейса и REST для ваших приложений Spring Boot. Apache OFBiz — еще один пример модульной монолитной и сервис-ориентированной архитектуры (SOA). Это комплексная система планирования ресурсов предприятия с сотнями таблиц и сервисов, которые могут автоматизировать бизнес-процессы предприятия. Несмотря на свои размеры, модульная архитектура позволяет разработчикам быстро разобраться и настроить ее.
|
Двухфазная фиксация
Распределенные транзакции обычно являются спасательным кругом и используются в различных случаях:- При записи на разрозненные ресурсы необходимо быть строго последовательным;
- Когда нам нужно писать в разнородные источники данных;
- Когда сообщение нужно обработать один раз и мы не можем провести рефакторинг системы и сделать ее операции идемпотентными;
- При интеграции со сторонними «черными ящиками» или устаревшими системами, реализующими спецификацию двухфазной фиксации.
Реализация архитектуры двухфазной фиксации.
Технические требования для двухфазной фиксации заключаются в том, что вам нужен менеджер распределенных транзакций, такой как Нараяна, и надежный уровень хранения журналов транзакций.
Вам также потребуются источники данных, совместимые с ДТП ХА, с соответствующими драйверами XA, которые могут участвовать в распределенных транзакциях, таких как СУБД, брокеры сообщений и кэши.
Если вам повезло иметь подходящие источники данных, но вы работаете в динамической среде, такой как Кубернетес , вам также понадобится специальный механизм, гарантирующий наличие только одного экземпляра менеджера распределенных транзакций.
Менеджер транзакций должен иметь высокую доступность и всегда иметь доступ к журналу транзакций.
В качестве примера реализации вы можете использовать Контроллер восстановления подснежника, который использует Шаблон StatefulSet Kubernetes гарантировать наличие одного элемента и постоянных томов для хранения журналов транзакций.
В эту категорию я также отношу такие характеристики, как атомарные транзакции веб-сервиса (WS-AtomicTransaction) для веб-служб SOAP. Все эти технологии объединяет то, что они реализуют спецификацию XA и имеют центрального координатора транзакций.
В нашем примере, показанном на рисунке 4, служба A использует распределенные транзакции для фиксации всех изменений в своей базе данных и сообщениях в очереди, не оставляя шансов на дублирование или потерю сообщений.
Аналогичным образом, служба B может использовать распределенные транзакции для получения сообщений и их фиксации в базе данных B за одну транзакцию без каких-либо дубликатов.
Или служба B может решить не использовать распределенные транзакции, а использовать локальные транзакции и реализовать шаблон идемпотентного потребителя.
Обратите внимание: подходящим примером для этого раздела будет использование WS-AtomicTransaction для координации операций записи в базу данных A и базу данных A в одной транзакции и полного предотвращения согласованности.
Но в наши дни такой подход встречается еще реже, чем тот, который я описал.
Рисунок 4. Двухэтапная фиксация между базой данных и брокером сообщений.
Преимущества и недостатки архитектуры двухфазных обязательств
Протокол двухфазной фиксации предлагает аналогичные гарантии для локальных транзакций при модульном монолитном подходе, но с некоторыми исключениями.Поскольку атомарное обновление включает два или более отдельных источников данных, они могут выйти из строя по отдельности и заблокировать транзакцию.
Но благодаря центральному координатору определить состояние распределенной системы по-прежнему легко по сравнению с другими подходами, которые я буду обсуждать.
В таблице 2 суммированы преимущества и недостатки этого подхода.
Преимущества | Стандартный подход с готовыми менеджерами транзакций и вспомогательными источниками данных.
Надежная согласованность данных для успешных сценариев.
|
Недостатки | Ограничения масштабируемости.
Возможные сбои восстановления в случае сбоя менеджера транзакций. Ограниченная поддержка источников данных. Требования к хранилищу и единый центральный координатор транзакций в динамических средах.
|
Примеры | API транзакций Джакарты (ранее API транзакций Java) WS-AtomicTransaction СТС/МИОП eBay ГРИТ Атомикос Нараяна Брокеры сообщений, такие как Apache ActiveMQ. Реляционные источники данных, реализующие спецификацию XA; хранилища данных в памяти, такие как Инфиниспан |
оркестровка
В модульном монолите мы используем локальные транзакции и всегда знаем состояние системы.Распределенные транзакции, основанные на протоколе двухфазной фиксации, также гарантируют согласованное состояние.
Единственным исключением может быть фатальный сбой с участием координатора транзакций.
Но что, если мы захотим упростить требования к согласованности, сохраняя при этом знание состояния всей распределенной системы и координируя ее из одного места? В этом случае мы можем рассмотреть оркестровка - подход, при котором одна из служб выступает в качестве координатора и организатора общего перехода к распределенному состоянию.
Служба оркестратора отвечает за вызов других служб до тех пор, пока они не достигнут желаемого состояния, или за принятие корректирующих мер в случае сбоя.
Оркестратор использует свою локальную базу данных для отслеживания изменений состояния и отвечает за восстановление после любых сбоев, связанных с изменениями состояния.
Реализация архитектуры оркестрации
Наиболее популярными реализациями методов оркестровки являются реализации спецификации BPMN, например проекты.Потребность в таких системах не исчезает из-за чрезмерно распределенных архитектур, таких как микросервисы или бессерверные архитектуры, а, скорее, возрастает. В качестве доказательства мы можем рассмотреть новые механизмы оркестровки с сохранением состояния, которые не соответствуют спецификации, но обеспечивают аналогичное поведение с сохранением состояния, например Netflix. Дирижер Убер Каденс и Апач Расход воздуха .
В эту категорию также попадают бессерверные архитектуры с отслеживанием состояния, такие как Amazon StepFunctions, Azure Durable Functions и Azure Logic Apps. Кроме того, существуют библиотеки с открытым исходным кодом, которые позволяют реализовать координацию с сохранением состояния и поведение отката, например реализацию шаблона Apache Camel. Сага и функциональность NServiceBus Сага .
Многие встроенные реализации шаблона Saga также попадают в эту категорию.
Рисунок 5. Организация распределенных транзакций между двумя сервисами В нашем примере диаграммы, показанной на рисунке 5, у нас есть Служба A, действующая как оркестратор с отслеживанием состояния, ответственный за вызов Службы B и восстановление после сбоев с помощью операции компенсации, если это необходимо.
Критической характеристикой этого подхода является то, что Служба A и Служба B имеют локальные границы транзакций, но Служба A обладает знаниями и ответственностью для управления общим потоком связи.
Вот почему граница его транзакции касается конечных точек службы B. С точки зрения реализации мы могли бы использовать синхронный подход, как показано на диаграмме, или асинхронный, используя очередь сообщений между службами (в этом случае вы также можете использовать двухфазную фиксацию).
.
Преимущества и недостатки оркестрации
Оркестрация — это подход к сквозной согласованности, который может включать повторные попытки и откаты для приведения системы в согласованное состояние.Хотя это позволяет избежать необходимости в распределенных транзакциях, оркестровка требует, чтобы участвующие службы предлагали идемпотентные операции в случае, если координатору придется повторить операцию.
Службы связи также должны предлагать конечные точки восстановления на случай, если координатор решит выполнить откат и исправить глобальное состояние.
Большим преимуществом этого подхода является возможность привести разнородные сервисы, которые могут не поддерживать распределенные транзакции, в согласованное состояние, используя только локальные транзакции.
Координатору и участвующим службам нужны только локальные транзакции, и всегда можно узнать состояние системы, обратившись к координатору, даже если система находится в частично согласованном состоянии.
Преимущества | Координирует состояние разнородных распределенных компонентов.
Нет необходимости в транзакциях XA. Известное распределенное состояние на уровне координатора.
|
Недостатки | Сложная модель распределенного программирования.
Могут потребоваться идемпотентность и компенсирующие операции от участвующих служб. Финальная последовательность. Возможны неисправимые сбои при компенсации.
|
Примеры | jBPM Камунда Микропрофиль Длительные действия Дирижер Netflix Убер Каденс Amazon СтепФункции Устойчивые функции Azure Реализация паттерна Apache Camel Saga. Реализация паттерна NServiceBus Saga. Спецификация CNCF Бессерверный рабочий процесс Встроенные реализации шаблона Saga |
Хореография
Как вы уже видели в обсуждении, одна бизнес-транзакция может привести к множеству вызовов между службами, и обработка бизнес-транзакции от начала до конца может занять неопределенное количество времени.Чтобы управлять этим, шаблон оркестрации использует централизованную службу контроллера, которая сообщает участникам, что делать.
Альтернативой оркестровке является хореография , стиль координации услуг, при котором участники обмениваются событиями без централизованного контроля.
При использовании этого шаблона каждая служба выполняет локальную транзакцию и публикует события, которые запускают локальные транзакции в других службах.
Каждый компонент системы участвует в принятии решений о рабочем процессе бизнес-операции, а не полагается на центральную точку контроля.
Исторически сложилось так, что наиболее распространенной реализацией хореографического подхода было использование уровня асинхронного обмена сообщениями для взаимодействия служб.
Рисунок 6 иллюстрирует базовую архитектуру шаблона.
Рисунок 6. Хореография сервиса с использованием уровня обмена сообщениями.
Двойная хореография
Чтобы хореография на основе сообщений работала, нам нужно, чтобы каждая участвующая служба выполняла локальную транзакцию и запускала следующую службу, публикуя команду или событие в инфраструктуре обмена сообщениями.Аналогично, другие участвующие службы должны использовать сообщение и выполнить локальную транзакцию.
Это само по себе является проблемой двойной записи в рамках проблемы двойной записи более высокого уровня.
Когда мы разрабатываем уровень обмена сообщениями с двойной записью для реализации подхода хореография , мы могли бы спроектировать его как двухфазную фиксацию, охватывающую локальную базу данных и брокер сообщений.
Я уже рассматривал этот подход. В качестве альтернативы мы можем использовать шаблон " опубликовать, а затем локальный коммит" или " локальная фиксация, затем публикация" :
- Опубликовать, а затем локальный коммит : Мы могли бы попытаться сначала опубликовать сообщение, а затем зафиксировать локальную транзакцию.
Хотя этот вариант может показаться отличным, у него есть практические проблемы.
Например, очень часто требуется опубликовать идентификатор, сгенерированный в результате локальной транзакции, который еще не доступен для публикации.
Кроме того, локальная транзакция может завершиться неудачно, если мы не сможем выполнить откат опубликованного сообщения.
В этом подходе отсутствует семантика чтения нашей записи, и он не является практическим решением для большинства случаев использования.
- Локальная фиксация, затем публикация : Немного лучше было бы сначала зафиксировать локальную транзакцию, а затем опубликовать сообщение.
Этот подход имеет небольшую вероятность неудачи после совершения локальной транзакции перед публикацией сообщения.
Но даже в этом случае вы можете сделать свои службы идемпотентными и повторить операцию, что будет означать повторную фиксацию локальной транзакции и последующую публикацию сообщения.
Этот подход может сработать, если вы контролируете последующих потребителей и можете сделать их идемпотентными.
Кроме того, это в целом хороший вариант реализации.
Хореография без двойной записи
Различные способы реализации архитектуры хореография ограничить каждую службу записью только в один источник данных с локальной транзакцией и никуда больше.Давайте посмотрим, как это может работать без двойной записи.
Допустим, Служба А получает запрос и записывает его в базу данных А и больше никуда.
Служба B периодически опрашивает службу A и обнаруживает новые изменения.
Когда служба B считывает изменение, она обновляет свою собственную базу данных, внося изменения, а также индекс или временную метку, в которую она получила эти изменения.
Критическим моментом здесь является тот факт, что обе службы записывают данные только в свою собственную базу данных и фиксируют результат с помощью локальной транзакции.
Этот подход, показанный на рисунке 7, можно описать как хореография сервисов или мы могли бы описать это, используя старую добрую терминологию конвейера данных.
Далее мы рассмотрим различные варианты реализации.
Рисунок 7: Хореография услуг посредством опроса Самый простой сценарий — подключить службу B к базе данных службы A и читать таблицы, принадлежащие службе A. Однако в отрасли стараются избегать такого уровня взаимодействия с общими таблицами, и на это есть веская причина: любые изменения в реализации службы A и модели данных.
может сломать службу B. Мы можем внести несколько дополнительных улучшений в этот сценарий, например, используя Шаблон исходящих сообщений и предоставление Сервису A таблицы, которая действует как общедоступный интерфейс.
Эта таблица может содержать только те данные, которые требуются службе B, и ее можно спроектировать так, чтобы ее можно было легко запрашивать и отслеживать изменения.
Если этого недостаточно, дальнейшим улучшением было бы, чтобы Служба Б запрашивала Службу А о любых изменениях через API плоскости управления вместо прямого подключения к базе данных A. По сути, все эти варианты имеют один и тот же недостаток: служба B должна постоянно опрашивать службу A. Это может привести к ненужной постоянной нагрузке на систему или ненужной задержке принятия изменений.
Опрос микросервиса на наличие изменений — сложная задача, поэтому давайте посмотрим, что мы можем сделать для дальнейшего улучшения этой архитектуры.
Хореография с Дебезием или альтернативами
Один из способов улучшить хореографическую архитектуру и сделать ее более привлекательной — внедрить такой инструмент, как Дебезий , который мы можем использовать для Теги: #Микросервисы #оркестрация #Распределенные системы #проектирование и рефакторинг #оркестровка микросервисов #транзакции #распределенные транзакции #хореография #конвейеры-
Совместимые Картриджи И Картриджи Oem
19 Oct, 24 -
Черная Пятница 2020 (Скидки На Хостинг)
19 Oct, 24 -
Современные Возможности Виртуализации
19 Oct, 24 -
Exocaptcha — Это Не Капча Для Вас!
19 Oct, 24 -
Google Ventures В Поисках Бессмертия
19 Oct, 24 -
Проектирование И Именование Очередей
19 Oct, 24 -
По 126 Подписчиков У Каждого
19 Oct, 24