Автор оригинала: Sergei Petunin.
1. введение
В этой статье мы обсудим варианты реализации транзакции между микросервисами.
Мы также рассмотрим некоторые альтернативы транзакциям в сценарии распределенных микросервисов.
2. Предотвращение Транзакций Между Микросервисами
Распределенная транзакция-это очень сложный процесс с большим количеством движущихся частей, которые могут выйти из строя. Кроме того, если эти части работают на разных машинах или даже в разных центрах обработки данных, процесс совершения транзакции может стать очень долгим и ненадежным.
Это может серьезно повлиять на пользовательский интерфейс и общую пропускную способность системы. Таким образом, один из лучших способов решить проблему распределенных транзакций – полностью избежать их.
2.1. Пример Архитектуры, требующей транзакций
Обычно микросервис спроектирован таким образом, чтобы быть независимым и полезным сам по себе. Он должен быть в состоянии решить какую-то атомарную бизнес-задачу.
Если бы мы могли разделить нашу систему на такие микросервисы, есть хороший шанс, что нам вообще не нужно было бы реализовывать транзакции между ними.
Например, давайте рассмотрим систему широковещательного обмена сообщениями между пользователями.
Микросервис user будет связан с профилем пользователя (создание нового пользователя, редактирование данных профиля и т. Д.) Со следующим базовым классом домена:
@Entity public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @Basic private String name; @Basic private String surname; @Basic private Instant lastMessageTime; }
Микросервис message будет связан с вещанием. Он инкапсулирует сущность Сообщение и все вокруг нее:
@Entity public class Message implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @Basic private long userId; @Basic private String contents; @Basic private Instant messageTimestamp; }
Каждый микросервис имеет свою собственную базу данных. Обратите внимание , что мы не ссылаемся на сущность User из сущности Message , поскольку классы пользователей недоступны из микросервиса message . Мы обращаемся к пользователю только по идентификатору.
Теперь сущность User содержит поле Время последнего сообщения , потому что мы хотим показать информацию о времени последней активности пользователя в ее профиле.
Однако, чтобы добавить новое сообщение пользователю и обновить его Время последнего сообщения , теперь нам придется реализовать транзакцию между микросервисами.
2.2. Альтернативный Подход Без Транзакций
Мы можем изменить нашу архитектуру микросервиса и удалить поле Время последнего сообщения из объекта User .
Затем мы могли бы отобразить это время в профиле пользователя, отправив отдельный запрос микросервису сообщений и найдя максимальное значение messageTimestamp для всех сообщений этого пользователя.
Вероятно, если микросервис message находится под высокой нагрузкой или даже не работает, мы не сможем показать время последнего сообщения пользователя в ее профиле.
Но это может быть более приемлемым, чем неспособность совершить распределенную транзакцию для сохранения сообщения только потому, что микросервис пользователя не ответил вовремя.
Конечно, существуют более сложные сценарии, когда нам приходится реализовывать бизнес-процесс в нескольких микросервисах, и мы не хотим допускать несоответствия между этими микросервисами.
3. Протокол Двухфазной Фиксации
Протокол двухфазной фиксации (или 2 ПК)-это механизм реализации транзакции между различными компонентами программного обеспечения (несколькими базами данных, очередями сообщений и т. Д.)
3.1. Архитектура 2PC
Одним из важных участников распределенной транзакции является координатор транзакций. Распределенная транзакция состоит из двух этапов:
- Фаза подготовки — во время этой фазы все участники транзакции готовятся к фиксации и уведомляют координатора о том, что они готовы завершить транзакцию
- Фаза фиксации или отката — во время этой фазы координатор транзакций выдает всем участникам команду фиксации или отката
Проблема с 2PC заключается в том, что он довольно медленный по сравнению со временем работы одного микросервиса.
Координация транзакций между микросервисами, даже если они находятся в одной сети, может действительно замедлить работу системы , поэтому этот подход обычно не используется в сценарии с высокой нагрузкой.
3.2. Стандарт XA
Стандарт XA представляет собой спецификацию для проведения распределенных транзакций 2PC между вспомогательными ресурсами. Любой JTA-совместимый сервер приложений (JBoss, GlassFish и т.д.) Поддерживает его из коробки.
Ресурсами, участвующими в распределенных транзакциях, могут быть, например, две базы данных двух разных микросервисов.
Однако, чтобы воспользоваться преимуществами этого механизма, ресурсы должны быть развернуты на единой платформе JTA. Это не всегда возможно для архитектуры микросервиса.
3.3. ОТДЫХ-ПРИ Стандартной Осадке
Другим предлагаемым стандартом является RESET-AT , который претерпел некоторую разработку Red Hat, но все еще не вышел из стадии проекта. Однако он поддерживается сервером приложений WildFly из коробки.
Этот стандарт позволяет использовать сервер приложений в качестве координатора транзакций с определенным REST API для создания и объединения распределенных транзакций.
Веб-службы RESTful, которые хотят участвовать в двухфазной транзакции, также должны поддерживать определенный API REST.
К сожалению, чтобы связать распределенную транзакцию с локальными ресурсами микросервиса, нам все равно придется либо развернуть эти ресурсы на одной платформе JTA, либо решить нетривиальную задачу написания этого моста самостоятельно.
4. Возможная согласованность и компенсация
Безусловно, одной из наиболее осуществимых моделей обеспечения согласованности между микросервисами является конечная согласованность .
Эта модель не обеспечивает выполнение распределенных транзакций ACID между микросервисами. Вместо этого он предлагает использовать некоторые механизмы обеспечения того, чтобы система в конечном итоге была согласованной в какой-то момент в будущем.
4.1. Обоснование возможной согласованности
Например, предположим, что нам нужно решить следующую задачу:
- регистрация профиля пользователя
- сделайте некоторую автоматическую проверку фона, чтобы пользователь действительно мог получить доступ к системе
Вторая задача-убедиться, например, что этот пользователь по какой-то причине не был запрещен на наших серверах.
Но это может занять некоторое время, и мы хотели бы извлечь его в отдельный микросервис. Было бы неразумно заставлять пользователя ждать так долго только для того, чтобы узнать, что он успешно зарегистрировался.
Одним из способов решения этой проблемы был бы подход, основанный на сообщениях, включая компенсацию. Давайте рассмотрим следующую архитектуру:
- микросервису user поручено зарегистрировать профиль пользователя
- микросервису validation поручено выполнить проверку фона
- платформа обмена сообщениями, поддерживающая постоянные очереди
Платформа обмена сообщениями может гарантировать, что сообщения, отправляемые микросервисами, сохраняются. Затем они будут доставлены позже, если получатель в настоящее время недоступен
4.2. Счастливый сценарий
В этой архитектуре счастливый сценарий был бы:
- микросервис user регистрирует пользователя, сохраняя информацию о нем в своей локальной базе данных
- микросервис user помечает этого пользователя флагом. Это может означать, что этот пользователь еще не прошел проверку и не имеет доступа к полной функциональности системы
- пользователю отправляется подтверждение регистрации с предупреждением о том, что не все функциональные возможности системы доступны сразу
- микросервис user отправляет сообщение в микросервис validation для проверки фона пользователя
микросервис validation выполняет проверку фона и отправляет сообщение пользователю микросервису с результатами проверки
- если результаты будут положительными, микросервис user разблокирует пользователя
- если результаты отрицательные, микросервис user удаляет учетную запись пользователя
После того, как мы пройдем все эти шаги, система должна быть в согласованном состоянии. Однако в течение некоторого периода времени пользовательская сущность, по-видимому, находилась в неполном состоянии.
Последний шаг, когда микросервис пользователя удаляет недействительную учетную запись, – это фаза компенсации .
4.3. Сценарии сбоев
Теперь давайте рассмотрим некоторые сценарии сбоев:
- если микросервис validation недоступен, то платформа обмена сообщениями с ее функцией постоянной очереди гарантирует, что микросервис validation получит это сообщение позже
- предположим, что платформа обмена сообщениями выходит из строя, а затем микросервис user пытается отправить сообщение снова в более позднее время, например, путем запланированной пакетной обработки всех пользователей, которые еще не были проверены
- если микросервис validation получает сообщение, проверяет пользователя, но не может отправить ответ обратно из-за сбоя платформы обмена сообщениями, микросервис validation также повторяет попытку отправки сообщения в более позднее время
- если одно из сообщений потерялось или произошел какой-либо другой сбой, микросервис user находит всех неподтвержденных пользователей с помощью запланированной пакетной обработки и снова отправляет запросы на проверку
Даже если некоторые сообщения были выданы несколько раз, это не повлияет на согласованность данных в базах данных микросервисов.
Тщательно рассматривая все возможные сценарии сбоев, мы можем гарантировать, что наша система будет удовлетворять условиям возможной согласованности. В то же время нам не нужно было бы иметь дело с дорогостоящими распределенными транзакциями.
Но мы должны отдавать себе отчет в том, что обеспечение конечной согласованности-это сложная задача. У него нет единого решения для всех случаев.
5. Заключение
В этой статье мы обсудили некоторые механизмы реализации транзакций между микросервисами.
И, в первую очередь, мы также изучили некоторые альтернативы этому стилю транзакций.