Статья-гайд от лида-аналитика "ITQ Group" Виталия Якубина.

Продолжением предыдущей статьи об архитектуре и интеграциях Архитектура приложений и интеграции: гайд по основным понятиям простыми словами / Хабр (habr.com), в данной статье будет освещен вопрос асинхронной интеграции. 

В статье будет описано, что такое асинхронная интеграция в общих чертах, и как она чаще всего реализуется. В первую очередь статья предназначена для аналитиков и начинающих разработчиков, чтобы сформировать общее представление по данному вопросу.

Еще раз о видах интеграций.

Итак, вспомним, что все виды интеграций можно разделить на 3 вида:

  1. Синхронная

  2. Асинхронная

  3. Смешанная

Синхронная интеграция, это такая  интеграция, при которой система, отправившая запрос (назовем её система-1), не продолжает свою работу до тех пор, пока не получит ответ от системы-обработчика запроса (система-2).

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

Асинхронная интеграция, это такая интеграция, при которой система-1 не дожидается ответа от системы-2. В данном случае ответ системе-1 может быть либо совсем не нужен. Например, если речь идёт о логировании действий, либо о ситуациях, когда получение ответа не влияет на дальнейшее выполнение функций системы.

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

Смешанная интеграция, как понятно из названия, сочетает в себе синхронную и асинхронную интеграцию.

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

Синхронные интеграции

Вопрос синхронных интеграций в общих чертах был освящен в предыдущей статье (см. в начале), поэтому еще раз разбираться с ними мы не будем.

Смешанные интеграции

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

Например, вы отправили запрос в банк на проведение платежа, через REST- сервис и тут же получили от сервиса ответ, что платеж принят, либо, что реквизиты получателя заполнены некорректно. Это синхронная часть.

Банк проводит платеж, а результат платежа отправляет вам, например, на e-mail или дает Вам возможность узнать статус своего платежа через REST сервис статусов платежей. Это асинхронная часть.

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

Асинхронные интеграции

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

Наиболее частый способ реализации асинхронной интеграции в мире современных микросервисов, это применение брокеров сообщений, таких как Kafka или RabbitMQ (подробно про данные брокеры можно почитать вот в этой статье RabbitMQ против Kafka: два разных подхода к обмену сообщениями / Хабр (habr.com) ). 

Итак, зачем же нам вообще нужно интегрироваться ассинхронно (с применением брокера)?

Слабая связанность сервисов.

Первое, что мы получим, это слабую связанность сервисов.

Расскажу более подробно, что имею в виду.

В случае применения очередей, наиболее часто применяется паттерн "издатель-подписчик" (о нём читаем тут: Издатель-подписчик (шаблон проектирования) — Википедия (wikipedia.org)). Основной посыл данного паттерна, в рамках объяснения слабой связанности сервисов, это то, что есть сервис-издатель, который публикует сообщения в некой очереди, и далее сервис судьбой сообщений не интересуется. Т.е количество подписчиков и алгоритм использования сообщений сервис-публикатор никак не задевают. В любой момент времени сообщения может читать один подписчик, тысяча, миллион или даже вообще никто не читать.

На примере той же многострадальной почты: Вы можете отправлять письма на адрес до востребования и никаких ошибок при отправке письма не получите. В свою очередь получатель может поинтересоваться, приходило ли ему сообщение и получить его, либо не интересоваться и не получать. Также он может попросить кого-то другого сходить за письмом. 

Легкость масштабирования.

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

Возможность реализации потоковых (workflow) шаблонов взаимодействия микросервисов.

Поток (workflow) – это фундамент любого многокомпонентного приложения. Информация проходит от источника через ряд процессов и в каком-то виде возвращается на последнем шаге. 

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

В классическом (не микросервисном) приложении его работа обеспечивается процессами приложения. В случае с микросервисами – микросервис запускается тогда, когда некая внешняя логика приняла решение его запустить. 

Рассмотрим доступные нам способы управления МС:

  1. Наиболее популярная модель управления МС -  это шаблон разработки API-брокер, в котором мастер-компонент (в данном случае брокер) получает задачу от процесса-источника и передает ее на выполнение микросервису.

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

Преимущества такого подхода в том, что логика обработка запроса сервисом отделена от логики потока, на этот раз в виде BPML. Это делает переиспользование компонентов более простым, потому что теперь каждый сервис это всего лишь шаг общего процесса. Также данный шаблон приближает нас к третьей модели -  шаблону оркестрации.

  1. Правила проектирования микросервисов говорят нам о том, что они должны быть гибкими и масштабируемыми. Это означает, что сервисы не должны хранить громоздкие сведения о состоянии от вызова до вызова. Также только последний сервис, получивший информацию на вход в рамках шага, может осуществить вызов следующего шага потока, т.е, если шаг предполагает использование нескольких микросервисов, то информацию, полученную от МС следующего шага может использовать только последний МС, вызванный в рамках текущего шага процесса. Это делает затруднительными масштабирование и повторное развертывание микросервисов.

В данном случае к нам на помощь приходит контроль состояний. КС в микросервисном приложении базируется  на отделении данных от логики. Т.е данные может получить любой связанный микросервису. При таком подходе элемент данных может:

А) происходить из воркфлоу-источника и изменяться в зависимости от состояния источника (front-end контроль состояний). Т.е, когда информация о нужном состоянии потока передается при запросе к микросервису;

Б) находиться в базе данных, доступной всем микросервисам и использоваться, когда поступила задача (back-end контроль состояний);

В) управляться конечно-шаговой машиной оркестрации.

Шаговая оркестрация описывает последовательность вызовов микросервисов в воркфлоу. Сервисные шины (Сервисная шина предприятия — Википедия (wikipedia.org)) – наиболее удачный пример такого подхода. Использование паттерна Свитча (Router Pattern </> TL;DR: JavaScript Codecasts (jonathanleemartin.com)), цель которого осуществить выполнение задачи  с помощью некой предопределенной последовательности процессов, в целом, передает смысл использования брокера. Но для более сложной оркестрации составных приложений важен вопрос контроля состояний.

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

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

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

Самое большое преимущество оркестрации машиной состояний  это то, что такой подход параллельно решает проблему масштабируемости и конкурентности. Каждый процесс получает данные и информацию о состоянии на вход, и мастер-процесс предоставляет информацию о состоянии и событии, которая нужна для активации соответствующего сервиса. Такая модель также является очень гибкой, поэтому она применяется в постоянных контейнерах и VM models  развертки и безсерверной развертке.

  1. Последний набор шаблонов, относящийся к наиболее общей модели микросервисного общения - это Service Mesh. Данный паттерн обычно используется применении ПО, такого как Istio and Linkerd.

    Слой Service Mesh - это выделенный уровень инфраструктуры, который обеспечивает связь между сервисами, автоматически шифрует соединения, обрабатывает повторные запросы и тайм-ауты.Также сервис может предоставлять телеметрию (коэффициент успешности, задержки) и многое другое.

    Подход к реализации интеграции через данный паттерн хорош тем, что все необходимые микросервису связи и балансировка нагрузки могут быть перемещены в детали реализации. Подробно можно прочесть здесь: Что такое service mesh простыми словами / Хабр (habr.com).

Выводы

Выбор того, какой способ интеграции выбрать часто базируется на best practices и частично зависит от инструментов (ПО), которые вы выберете. Чаще всего наиболее удачное решение - это использование RESTful API и проектирование в рамках сервисов, а не действий. JSON и XML при таком подходе – самые распространенные форматы передачи данных .

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

При разработке API стоит учитывать потребности всех процессов и приложений, которые могут использовать данный МС. Многие разработчики МС оттачивают свое мастерство в разработке машины состояний из-за её гибкости, и потому что при ее использовании создается модель данных, полностью описывающая состояние процесса и переменные в одном месте. Это делает отладку значительно проще. Но какой бы подход Вы не выбрали – не забывайте быть последовательным, иначе позже Вы столкнетесь с проблемами отладки.

Также дополню, что  я согласен с точкой зрения, что асинхронную интеграцию можно в свою очередь поделить на обычную и реактивную (об этом можно почитать тут Интеграция: синхронное, асинхронное и реактивное взаимодействие, консистентность и транзакции / Хабр (habr.com)) , но подробно разбирать данный вопрос в этой статье не будем.

Спасибо за прочтение! Любая обратная связь по материалам данной статьи приветствуется!

Материалы

При подготовке статьи использованы материалы сайтов:

https://www.techtarget.com/ .

API workflow design patterns for microservices scenarios | TechTarget

Synchronous vs. asynchronous microservices communication patterns | TheServerSide

What are synchronous/asynchronous APIs? – Definition from TechTarget

Комментарии (3)


  1. Theon4eg
    18.04.2023 21:51
    +2

    Позволю себе несколько замечаний к статье.

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

    2. Ссылка на вики "издатель-подписчик" не работает;

    3. "Смешанная интеграция" - это уже прием для реализации флоу бизнес процесса, я бы не выделял это в отдельный вид, ну или оформил бы как вариацию асинхрона;

    4. Вы говорите, что шаблон издатель-подписчик это пример использования очередей и в качестве иллюстрации упоминаете почту, но это некорректно: реализация Pub-Sub шаблона это, скорее, радиоэфир, когда много подписчиков подключаются к топику одного издателя и получают сообщение, пока подключены. Почта - хороший пример "Очереди", но это паттерн Уведомление (или запрос-ответ, если там двусторонняя переписка)

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

    6. Паттерн стейт-машина больно уж мудрено описан

    7. Не согласен с тем, что сервисная шина это пример реализации оркестрации. Суть оркестрации в том, что есть стейтфул оркестровщик, который хранит состояние выполняемого процесса и в зависимости от этого состояния вызывает нужные сервисы. А сервисная шина - это, скорее, "умный" маршрутизатор запросов от системы А к одной и систем из списка на основании некоторых правил, которые учитывают занные в запросе.

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

    9. Там же, в выводах: RESTful API это не противопоставление сервисов действиям: RPC API тоже может реализовывать сервисную модель.

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


    1. vitalik_yakubin
      18.04.2023 21:51

      Спасибо за содержательный комментарий!

      1. По п . 1 хотел дать общее представление о том, как вообще можно асинхронно интегрировать приложение. Изначально думал рассказать о том, что применяется для асинхрона, в плане технологий и про Стейт машину. Большая часть текста возникла уже в ходе изучения статей по теме и я понял, что в одну статью это не уместить, поэтому попытался верхнеуровнево описать смысл технологии/подхода для того, чтобы читатель просто узнал, что такое есть и оставить ссылку на материал для подробного самостоятельного изучения

      2. По п. 2 поправим

      3. По п. 3 я подумаю, как поменять формулировку

      4. Да, про шаблон издатель-подписчик согласен. Подойдет пример с доской объявлений. Поправим

      5. Внесу пояснения

      6. Подумаю как упростить

      7. Поправлю

      8. Тоже поправлю

      9. Тоже подумаю над правками.

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


      1. Theon4eg
        18.04.2023 21:51

        Хорошо)