Я читал несколько статей и вопросов о возможной согласованности и хореографии микросервисов, но не нашел четкого ответа на этот вопрос. Я сформулирую это в общих чертах.
В двух словах: если клиент исторически выполнял последующие синхронные вызовы REST к вашей системе, что вы делаете, когда более поздние вызовы могут возвращать неожиданные результаты после выполнения вызовов к различным микросервисам (из-за согласованности в конечном итоге) ?
Проблема
Предположим, у вас есть монолитное приложение, предоставляющее REST API. Допустим, есть два модуля A и B, которые вы хотите преобразовать в микросервисы. Сущности, которые поддерживает B, могут ссылаться на сущности, которые поддерживает A (например, A поддерживает студентов, а B поддерживает классы). В монолитной ситуации модули просто ссылаются на одну и ту же базу данных, но в ситуации с микросервисами каждый из них имеет свою собственную базу данных и взаимодействует с помощью асинхронных сообщений. Таким образом, их базы данных в конечном итоге согласуются друг с другом.
Некоторые существующие сторонние клиентские приложения нашего API используются для первого (синхронного) вызова конечной точки, принадлежащей модулю A, и после того, как этот первый вызов возвращается, немедленно (т.е. через несколько мс) вызова конечной точки в модуле B как часть их рабочий процесс (например, создание ученика и помещение его в класс). В новой ситуации это приводит к проблеме: когда происходит второй вызов, модуль B может еще не знать об изменениях в модуле A. Таким образом, существующий рабочий процесс клиентского приложения может сломаться. (Например, модуль B может ответить: учащегося, которого вы пытаетесь поместить в класс, не существует или он учится не на том курсе.)
Когда вызовы выполняются отдельно пользователем-человеком через какое-то внешнее приложение, это не является большой проблемой, так как модули обычно в любом случае согласованы через секунду. Проблема возникает, когда клиентское приложение (которое не находится под нашим контролем) просто вызывает A, а затем немедленно B в рамках автоматизированного рабочего процесса. Конечная согласованность просто недостаточно быстра в этом случае.
Простая диаграмма, описывающая ситуацию
Вопрос
Существует ли передовая практика или общепринятый набор вариантов для смягчения этой проблемы? (Я придумал пример ученика/класса, не зацикливайтесь на его специфике. :))
Что мы можем думать о
- Просто скажите разработчикам этих клиентов: с этого момента вы должны реализовать механизм повторных попыток для каждой вызываемой вами конечной точки. Недостаток кажется очевидным.
- Реализуйте шлюз API, который ожидает, пока B не будет готов. Недостаток: существует множество возможных рабочих процессов (с участием большего количества модулей от A до Z), которые потребуют этого, поэтому шлюз может стать довольно сложным.
- Каким-то образом создайте сеанс для клиента, который отслеживает, какие запросы он сделал последовательно. Затем B может выяснить, должен ли он ждать сообщения от A, или он может даже обновить свое состояние, просто просмотрев точный запрос, который клиент сделал для A.
Есть ли лучшие методы? Какой будет наиболее подходящим?
Изменить: уточнено, что вопрос в первую очередь касается поведения сторонних клиентов, которые автоматически вызывают конечные точки, а это означает, что даже несколько миллисекунд «задержки» в возможной согласованности могут быть фатальными.