Как правильно синхронизировать данные между микросервисами?

19

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

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

Однако, что, если Bпроисходит сбой / сбой и через некоторое время возвращается снова. За это время Aопубликовал еще два сообщения. Как Bузнать, как обновить локальную копию Aинформации?

Конечно, если он Bявляется единственным потребителем в Aочереди, он может начать читать его, как только вернется в оперативный режим, но что, если в этой очереди есть другие потребители и эти сообщения потребляются?

В качестве более конкретного примера, если у Usersслужбы обновляется адрес электронной почты, когда Billingмикросервис не работает, если Billingмикросервис возвращается снова, как он узнает, что электронная почта обновлена?

Когда возвращаются микросервисы, происходит ли трансляция с сообщением «Эй, я вернулся, дай мне всю свою текущую информацию?»

В целом, каковы лучшие отраслевые практики для синхронизации данных?

noblerare
источник
1
Чтобы избежать этого, когда это возможно.
Теластин
1
Почему Ordersнужно что-то знать о Users?
kdgregory
Это просто пример. Замените два на то, что вы хотите, что имеет смысл.
noblerare
маршрутизация через веер решит вашу проблему «сообщение уничтожено кем-то другим». но на самом деле неясно, чего вы пытаетесь достичь.
Эван
@Ewan Я обновил свой оригинальный пост, чтобы лучше объяснить, что я пытаюсь спросить.
Noblerare

Ответы:

5

Я бы поставил под сомнение всю вашу идею «передачи данных на все другие микросервисы».

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

Дж. Фабиан Мейер
источник
Я думаю, что этот ответ совершенно правильный. Это устраняет множество проблем, связанных с синхронизацией. На самом деле, я сейчас смотрю на код, у которого есть такие проблемы, потому что разные сервисы хранят копии информации и имеют такие проблемы с синхронизацией.
DaveG
2
Спасибо за Ваш ответ. Так зачем тогда нужна модель паба / подмодели и очереди сообщений? Если мы пытаемся «вытянуть» данные, а не «вытолкнуть», мы беспокоимся о задержке обслуживания.
Noblerare
AFAIK, ваш сервис не должен реагировать немедленно, если что-то меняется (как в пабе / сабе), но иногда нужны данные. Тогда я бы просто потянул это. Если вы беспокоитесь о задержке, вы можете кэшировать данные, но это опять-таки происходит за счет того, что вы не знаете, актуальны ли данные. Если у вас большие файлы, вы также можете спросить, изменилось ли что-нибудь, прежде чем снова что-то вытягивать.
Дж. Фабиан Мейер
Имейте в виду, что это решение стоит дорогой привязки зависимой службы, что означает, что адрес электронной почты будет недоступен, когда служба пользователя недоступна. Одна из первоначальных идей создания сервисов с самого начала, чтобы они были независимо развертываемыми, масштабируемыми и т. Д. Если все сервисы взаимодействовали напрямую друг с другом без кэша или гарантии высокой доступности, то когда одна система не работает, все они опускаться.
Dukethrash
@dukethrash Тогда сделайте их очень доступными.
Дж. Фабиан Мейер
5

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

Event-источников

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

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

Apache Kafka как брокер событий

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

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

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

Саги

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

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

noblerare
источник
Я до сих пор не понимаю, почему вы хотите построить такую ​​сложную структуру. Обычно гораздо проще, если каждая служба просто хранит свои собственные данные и передает их другим службам по запросу.
Дж. Фабиан Мейер
^ Но это снизит доступность системы. Сложная конструкция может быть оправдана, если требуется высокая устойчивость.
Авмохан
1

Даже если я опоздаю, я бы поставил свои 2 цента на аргумент, потому что я думаю, что это важный момент, когда вы хотите оценить и разработать архитектуру управляемых событиями микросервисов. Каждый микросервис точно знает, какие события влияют на его состояние, и способен их ждать. Когда микросервис недоступен, должен существовать компонент, который хранит сообщения, необходимые от неисправного микросервиса, до тех пор, пока он не сможет их «потреблять». На самом деле это модель «производитель / потребитель», а не «публикация / подписка». Брокеры сообщений (такие как Kafka, RabbitMQ, ActiveMQ и т. Д.) Обычно являются наилучшим способом достижения такого поведения (если вы не реализуете что-то другое, например, получение событий), обеспечивая постоянные очереди и механизм подтверждения.

Теперь микросервис знает, что сообщение в конечном итоге доставляется, но этого недостаточно: каким образом он ожидает доставку одного сообщения? может ли он управлять доставкой нескольких копий одного и того же уведомления о событии? Это вопрос доставки семантический (хотя бы один раз, ровно один раз)

Последние мысли):

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

  2. Даже брокер может потерпеть неудачу, в этом случае сообщения теряются

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

Кармин Ингалди
источник
0

Вы можете заменить обычную очередь событий моделью издателя / подписчика, где Aслужба публикует новое сообщение темы T, а Bтип микросервисов будет подписываться на ту же тему.

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

A.Rashad
источник
0

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

Если вы хотите, чтобы B мог получить доступ к внутренним данным A, вам лучше всего дать ему доступ к внутренним базам данных A.

Однако вам не следует этого делать, весь смысл сервис-ориентированной архитектуры состоит в том, что сервис B не может видеть внутреннее состояние сервиса A и ограничен в выполнении запросов через API REST (и наоборот).

В вашем случае вы можете иметь службу пользовательских данных, которая несет ответственность за хранение всех пользовательских данных. Другие службы, которые хотят использовать эти данные, запрашивают их только тогда, когда им это нужно, и не хранят локальную копию (что, кстати, действительно полезно, если вы думаете о соответствии GDPR). Служба пользовательских данных может поддерживать простые операции CRUD, такие как «Создать нового пользователя» или «Изменить имя для user_id 23», или может иметь более сложные операции: «Найти всех стандартных пользователей, у которых в ближайшие 2 дня будет день рождения, и дать им». Премиум пробный статус ". Теперь, когда вашей платежной службе нужно отправить электронное письмо пользователю 42, она спросит службу пользовательских данных «Какой адрес электронной почты для user_id 42», использует свои внутренние данные со всей платежной информацией для обработки электронной почты, а затем может передать адрес электронной почты и тело почтового сервера.

Елена
источник