robinzonejob
@robinzonejob
разработчик .NET

Кто-нибудь может помочь разобраться с асинхронным взаимодействием между микросервисами?

Приведу сферический пример из головы, чтобы было нагляднее:

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

В первом микросервисе происходит создание карточки студента, заполнение её персональными данными и прикрепление фотографии. Иными словами есть WEB API и три метода, которые вызываются в сл. порядке:

1. CreateStudentCard()
2. PutPersonalData()
3. PutPhoto()

В первом методе CreateStudentCard() происходит создание карточки студента, генерация её ID и сохранение в базу с этим ID, в качестве первичного ключа.
В остальных двух методах мы, используя полученный ID карточки студента в качестве внешнего ключа, сохраняем в другие соотв. таблицы персональные данные и фото.

В каждом из этих трех методов, после сохранения в базу, происходит отправка события на шину RabbitMQ с использованием библиотеки MassTransit. В первом методе, после сохранения карточки в базу, происходит отправка сообщения StudentCardCreated, которое содержит ID карточки и дату создания. Во втором методе, соответственно, отправка события PersonalDataUpdated с данными студента, в третьем PhotoUpdated с фотографией.

Во втором микросервисе мы подписаны на все эти три события. Иными словами - есть три асинхронных Consumer'а, которые получат асинхронно три эти сообщения для того, что бы уже в своей базе создать карточку студента, с тем же ID, что и в микросервисе источнике, и прикрепить к ней персональные данные и фото.

И вот тут начинается суть вопроса:

Каждый асинхронный consumer-потребитель в своей реализации имеет методы обращения к контексту данных, которые так же асинхронны (в частности LINQ-методы EntityFramework).
И вот первый consumer, который прослушивает событие StudentCardCreated получает сообщение. Внутри него мы, прежде чем сразу взять и сохранить пришедшую карточку, вызываем какой-то асинхронный метод, для проверки чего-либо в базе, используя await (допустим AnyAsync). Как только в этом consumer'e вызывается await, управление вернется вызывающему потоку, который тут же подхватит следующее событие из очереди и вызывает consumer для второго сообщения PersonalDataUpdated.
В таком случае есть риск, что consumer для PersonalDataUpdated дойдет до сохранения в базу данных раньше, чем создастся сама карточка студента в consumer'e для StudentCardCreated, что, разумеется приведет к ошибке, так как нам нужен ID карточки в качестве внешнего ключа, ведь мы не можем прикрепить данные и фото к карточке, которой не существует в базе.

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

Набросал в пейнте схему того, что я имею в виду. Стрелочками и цифрами обозначен тот порядок действий, как я его вижу:
615d65c280b84125174838.png
  • Вопрос задан
  • 199 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы