Как интегрировать в транзакцию разные «микросервисы»?

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

Я использую микросервисы с некоторой долей скепсиса, потому что мы не полностью принимаем концепции для определения настоящих микросервисов. Одно (и я думаю, самое большое) различие заключается в том, что мы используем одну и ту же общую базу данных для разных модулей (которые я называю «микросервисами»). Что-то вроде логического представления нашей системы можно было бы изобразить следующим образом:

                  ╔══════════════╗
                  ║    Client    ║ ══╗
                  ╚══════════════╝   ║ (2)
                                     ║
                                     ▼        
        ╔══════════════╗  (1) ╔══════════════╗
        ║  Serv. Reg.  ║ <==> ║  API Gatew.  ║
        ╚══════════════╝      ╚══════════════╝
            █       █   █████████████     (4)
           █         █              ████
╔══════════════╗  ╔══════════════╗  ╔══════════════╗
║   Module A   ║  ║   Module B   ║  ║   Module C   ║  <===== "Microservices"
╚══════════════╝  ╚══════════════╝  ╚══════════════╝
        ║║ (3)           ║║ (3)            ║║ (3)
        ║║               ║║                ║║
╔══════════════════════════════════════════════════╗
║                Database Server                   ║
╚══════════════════════════════════════════════════╝

Некоторые вещи, которые мы уже выяснили:

  • Клиенты (внешние системы, внешние приложения) будут получать доступ к различным внутренним модулям, используя шаблон обнаружения / маршрутизации. Мы рассматриваем сочетание Netflix OSS Eureka и Zuul, чтобы обеспечить это. Службы (модули A, B, C) регистрируются (4) в модуле регистрации служб, а шлюз API координирует (1) с регистром, чтобы найти экземпляры служб для выполнения запросов (2).
  • Все разные модули используют одну и ту же базу данных. (3) Это скорее запрос клиента, чем архитектурное решение.

Дело в том, что мы (или я лично) застряли в том, как установить связь между различными модулями. Я прочитал массу различных шаблонов и анти-шаблонов для этого, и почти каждый скажет, что интеграция API через RestTemplate или какой-либо специализированный клиент, такой как Feign или Ribbon.

Мне этот подход не нравится по некоторым причинам, в основном из-за синхронности и отсутствия состояния HTTP-запросов. Природа HTTP без сохранения состояния - моя самая большая проблема, поскольку уровень обслуживания различных модулей может иметь сильные привязки. Например, действие, которое запускается в модуле A, может иметь разветвления для модулей B и C, и все должно быть скоординировано с точки зрения «транзакции». Я действительно не думаю, что HTTP был бы лучшим способом контролировать это!

Часть Java EE внутри меня кричит, чтобы использовать какую-то интеграцию служб, такую ​​как EJB или RMI, или что-нибудь, что в конечном итоге не использует HTTP. Для меня было бы гораздо более «естественным» подключить определенную службу из модуля B внутрь модуля A и быть уверенным, что они вместе участвуют в транзакции.

Еще одна вещь, которую необходимо подчеркнуть, - это то, что для нашего клиента недостаточно парадигм, таких как возможные несоответствия в базе данных, поскольку они имеют дело с некоторыми серьезными данными. Итак, фраза «Я обещаю сделать все возможное с данными» здесь не очень хорошо.

Время для вопроса:

Действительно ли эта «интеграция сервисов» имеет место при работе с «микросервисами»? Или «Интеграция ресурсов» побеждает?

Кажется, что Spring, например, предоставляет Spring Integration для обмена сообщениями между службами, как это делает такая технология, как EJB. Это лучший способ интегрировать эти сервисы? Я что-то упускаю?

PS: Вы можете называть мои «микросервисы» «микролитами», как мы обычно называем их здесь. :)

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

dunni 17.01.2019 08:14

@dunni: да, я тоже считаю, что микросервисная архитектура - не лучший способ описать то, чего мы пытаемся достичь. Все разные службы (модули) используют одну и ту же базу данных и одну и ту же схему в базе данных. Основная цель этой архитектуры - иметь более мелкие части целого - например, микролит, - чтобы их можно было легко масштабировать.

Gustavo Ramos 17.01.2019 08:19

Вам может помочь: stackoverflow.com/questions/9795677/…

Mehraj Malik 17.01.2019 08:41

@MehrajMalik не совсем то, что я ищу. Эта ссылка относится к Java Transaction API и шаблонам Java EE для реализации глобальных транзакций. Мой вопрос больше касается транзакций в распределенных системах без с использованием Java EE, для большей свободы и гибкости.

Gustavo Ramos 17.01.2019 08:44
3
4
786
3

Ответы 3

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

Это означает, что микросервис реализован как «эксперт» некоторого понятия домена из вашей бизнес-логики, который является достаточно широким / сложным, чтобы «заслужить» микросервис.

В противном случае теряются многие преимущества микросервисной архитектуры:

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

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

Вы видите, куда он идет ...

Теперь вопрос транзакций или, в более общем смысле, целостности данных действительно является одной из самых больших проблем, которые необходимо решить при переходе к подходу микросервисов.

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

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

Проблема с этим подходом заключается в том, что в отраслях со тесно связанными доменами (например, у меня), если бы мы действительно хотели изолировать блоки данных от разных сервисов, полученные сервисы ни в коем случае не были бы микро! Например: в базе данных одного из приложений, которые мы разработали в прошлом, было ~ 350 сущностей. К некоторым центральным объектам - их может быть около 50 - практически доступны практически все функции системы. Когда мы думаем о расширении сервиса, чтобы по-настоящему изолировать домен, я думаю, что сама суть «микро» теряется. Идеи?

Gustavo Ramos 17.01.2019 08:40

Что касается общих данных, Крис Ричардсон представляет шаблон общая база данных в архитектуре микросервисов. Это действительно так? Кажется, он вполне справляется по этому вопросу, но в любом случае лучше спросить!

Gustavo Ramos 17.01.2019 08:42

Хорошая ссылка, но прочтите его собственный комментарий, когда люди спрашивают об ограничении количества подключений: этот шаблон скорее анти-шаблон. Лучше всего использовать шаблон «База данных для каждой службы». Каждая служба имеет частную схему (возможно, на том же сервере базы данных), и транзакции не охватывают схемы. Таким образом вы можете просто добавить экземпляры базы данных.

Mark Bramnik 17.01.2019 08:52

Да, ты прав. Вы видите один из моих последних ответов на OP? Моя проблема с базой данных для каждой службы заключается в том, что наш домен очень сильно связан. Если я пойду этим путем, то полученный сервис будет совсем не микро!

Gustavo Ramos 17.01.2019 08:55

По поводу вашего первого комментария. действительно микросервисы не для всех. Однако очень часто случается, что из-за монолита на самом деле легко создать эти взаимозависимости и «получить доступ ко всем функциям системы» - что является беспорядочным подходом независимо от архитектуры. Итак, через пару лет система стала настолько взаимосвязанной, что разложение микросервисов стало очень трудным. Так что это работает в противоположном направлении.

Mark Bramnik 17.01.2019 08:56

Вы можете заглянуть в Domain-Driven Design, чтобы подумать о контекстах ваших услуг. Агрегат должен быть вашей границей для транзакции.

Чтобы подумать о взаимодействии между службами, вы можете попытаться идентифицировать Events и использовать их для координации изменений вашего состояния в нескольких службах. Если вам действительно нужно много данных из нескольких внешних служб для выполнения транзакции, вы можете сохранить копию внешних данных в службе, которая нуждается в данных. Projections они обычно вызываются и могут обновляться событиями, публикуемыми внешними службами.

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

Подход Микросервисы становится очевидным при проектировании, ориентированном на предметную область. Прежде всего, вы должны иметь глубокие знания о том, к какому домену обращается ваше приложение. И должен иметь возможность идентифицировать ограниченный контекст модулей ваших приложений. Если вы определили такие гранулированные модули, вы можете легко преобразовать их в микросервис. Каждый ограниченный контекст должен быть в высшей степени независимым, это означает, что отдельная транзакция должна обрабатываться внутри микросервиса. Поскольку вы используете общую базу данных, я предполагаю, что это СУБД, строго соответствующая свойству КИСЛОТА. Если вам нужно получить свойство ACID для ваших транзакций с БД, каждый микросервис, который вы собираетесь создать, должен обрабатывать управление транзакциями независимо. С другой стороны, если вы можете концептуализировать транзакции как последовательность событий, и каждое событие само по себе поддерживает свое состояние, вы можете развить свои микросервисы как механизмы для обработки этих событий. Если вы получите дизайн, в котором микросервисы должны сильно зависеть друг от друга, и они не могут самостоятельно обрабатывать состояния / события / транзакции в большинстве случаев использования, вам придется переосмыслить свой подход. То, что вы пытаетесь достичь, называется шаблон общей базы данных. Некоторые люди считают это антипаттерн.

Другие вопросы по теме