Подтвердить что ты не робот

Согласованность БД с микросервисами

Каков наилучший способ достижения согласованности БД в системах на основе микросервисов?

В GOTO в Берлине Мартин Фаулер говорил о микросервисах, и одно "правило", о котором он говорил, заключалось в том, чтобы хранить базы данных "за услугу", что означает, что службы не могут напрямую подключаться к БД, принадлежащей другой службе.

Это супер-красиво и элегантно, но на практике это становится немного сложнее. Предположим, что у вас есть несколько сервисов:

  • интерфейс
  • служба управления заказами
  • услуга программы лояльности

Теперь клиент совершит покупку на вашем интерфейсе, который вызовет сервис управления заказами, который сохранит все в БД - без проблем. На этом этапе также будет призыв к службе лояльности, чтобы он выдавал кредиты/дебетовые баллы из вашей учетной записи.

Теперь, когда все находится на одном сервере БД/БД, все становится проще, так как вы можете запускать все в одной транзакции: если служба программы лояльности не может записать в БД, мы можем вернуть все это.

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

Я очень хочу услышать ваши предложения!.. и спасибо заранее!

4b9b3361

Ответ 1

Это супер-красиво и элегантно, но на практике это становится немного сложным

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

что службы не могут напрямую подключаться к БД, принадлежащей другой службе.

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

Теперь, на ваш вопрос:

Каковы наилучшие модели, чтобы поддерживать последовательность и жить счастливой жизнью?

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

Если вы используете .NET, некоторые примеры инфраструктуры, которые поддерживают такую ​​надежность, включают NServiceBus и MassTransit. Полное раскрытие информации - я основатель NServiceBus.

Обновление:. Следуя замечаниям относительно проблем с точками лояльности: "если изменения баланса обрабатываются с задержкой, клиент может фактически заказать больше предметов, чем у них есть очки".

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

Ответ 2

Я обычно не занимаюсь микросервисами, и это может быть не очень хорошим способом, но есть идея:

Чтобы переформулировать проблему, система состоит из трех независимых, но сообщающихся частей: интерфейс, бэкэнд управления заказами и бэкэнда программы лояльности. Интерфейс хочет убедиться, что какое-то состояние сохраняется как в бэкэнде управления заказами, так и в бэкэнде программы лояльности.

Одним из возможных решений было бы реализовать некоторый тип двухфазного фиксации:

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

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

  • Если сбой на первом шаге не изменится, данные не будут изменены.
  • Если он не работает во втором, третьем, четвертом или пятом случае, когда система возвращается в сеть, он может сканировать все внешние записи, ища записи без соответствующего идентификатора транзакции (любого типа). Если он встречается с любой такой записью, он может воспроизводиться, начиная с шага 2. (Если на шаге 3 или 5 произошел сбой, в бэкэндах будут оставлены оставленные записи, но он никогда не будет перемещен из промежуточной области, поэтому это нормально.)
  • Если он не работает на шестом, седьмом или восьмом шаге, когда система возвращается в сеть, он может искать все внешние записи с заполненными идентификаторами транзакций. Затем он может запросить серверы, чтобы увидеть состояние этих транзакций, совершенных или незафиксированных. В зависимости от того, что было сделано, оно может возобновиться с соответствующего шага.

Ответ 3

Я согласен с тем, что сказал @Udi Dahan. Просто хочу добавить к его ответу.

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

1) Измените отказ API API лояльности. То есть он может продолжать запросы, чтобы они не потерялись и могут быть восстановлены (повторно выполнены) в какой-то более поздний момент.

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

3) Сделайте то, что сказал Уди, и разместите его на хорошей очереди (точнее, паб/поднабор). Обычно это требует, чтобы абонент выполнял одну из двух задач... либо сохранял запрос перед удалением из очереди (goto 1) -OR-- сначала заимствовать запрос из очереди, а затем после успешной обработки запроса получить запрос удалено из очереди (это мое предпочтение).

Все трое выполняют одно и то же. Они пересылают запрос на постоянное место, где его можно проработать до успешного завершения. Запрос никогда не теряется и, если необходимо, повторится до достижения удовлетворительного состояния.

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

Ответ 4

Даже для распределенных транзакций вы можете попасть в "транзакцию с сомнительным статусом", если один из участников обрушится посреди транзакции. Если вы проектируете сервисы как идемпотентную операцию, жизнь становится немного проще. Можно писать программы для выполнения бизнес-условий без XA. Пэт Хелланд написал превосходную статью по этому поводу под названием "Life Beyond XA". В основном подход заключается в том, чтобы сделать как можно более минимальные предположения о удаленных объектах. Он также иллюстрировал подход, называемый Open Nested Transactions (http://www.cidrdb.org/cidr2013/Papers/CIDR13_Paper142.pdf) для моделирования бизнес-процессов. В этом конкретном случае транзакция покупки будет потоком верхнего уровня, а лояльность и управление заказами станут потоками следующего уровня. Хитрость заключается в том, чтобы разбивать гранулированные службы как идемпотентные сервисы с логикой компенсации. Поэтому, если какая-либо вещь не срабатывает в любом месте потока, отдельные службы могут ее компенсировать. Так, например, если заказ по какой-то причине не удается, лояльность может вычесть начисленную сумму для этой покупки.

Другой подход заключается в моделировании с использованием возможной согласованности с использованием CALM или CRDT. Я написал блог, чтобы выделить CALM в реальной жизни - http://shripad-agashe.github.io/2015/08/Art-Of-Disorderly-Programming Может быть, это поможет вам.