November 20, 2023

Передача данных между микросервисами

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

Общий источник данных - это плохо

Главная причина - вы теряете свободу внесения изменений в схему данных. Представим, что за каждый микросервис отвечает отдельная команда. Если каким-то нашим микросервисам нужны данные о продуктах - они должны обращаться к микросервису Products через его API. Таким образом, мы считаем, что API этого микросервиса является единственным достоверным источником данных. Как только какой-то из потребителей получит возможность обращаться напрямую к БД - достоверность пропадёт. Структура данных, их типы в API и в БД одного микросервиса могут отличаться. Кроме того, дав доступ напрямую к БД вы завяжите потребителей на текущую схему, версионировать её будет сложнее, чем версионировать API.

Общие данные - это хорошо

Например, сервису поездок нужны данные из сервиса пассажиров и сервиса водителей.

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

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

Синхронное взаимодействие и строгая согласованность не масштабируется

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

Рассмотренный нами пример является достаточно синтетическим - зачастую, сервисы обращаются к бОльшему количеству других сервисов, а те в свою очередь ещё к нескольким.

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

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

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

Согласованность в конечном счёте

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

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

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

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

Основано на - https://medium.com/@denhox/sharing-data-between-microservices-fe7fb9471208