Консистенция данных через микросервисы

В то время как каждая микросервис обычно будет иметь свои собственные данные - определенные объекты должны быть согласованы между несколькими службами.

Для такого требования к согласованности данных в высокораспределенном ландшафте, таком как архитектура микросервисов, каковы варианты дизайна? Конечно, мне не нужна архитектура общей базы данных, где один БД управляет состоянием во всех сервисах. Это нарушает принципы изоляции и общего принципа.

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

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

Может ли Akka или любая другая инфраструктура решить этот вариант использования? Как?

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

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

Ответ 1

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

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

  • Рассмотрим выражение зависимости, используя служебные интерфейсы (API) вместо прямой зависимости от БД. Это позволит каждой сервисной команде изменять свою внутреннюю схему данных столько, сколько требуется, и беспокоиться только о дизайне интерфейса, когда дело касается зависимостей. Это полезно, потому что проще добавлять дополнительные API и медленно обесценивать старые API вместо изменения дизайна БД вместе со всеми зависимыми Microservices (возможно, в то же время). Другими словами, вы по-прежнему можете развертывать новые версии Microservice независимо, если старые API все еще поддерживаются. Это подход, рекомендованный техническим директором Amazon, который был новатором многих подходов Microservices. Здесь рекомендуется прочитать интервью Kafka.

Ответ 2

Теоретические ограничения

Одним из важных моментов, которые следует помнить, является теорема CAP:

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

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

Распределенные данные Akka

Akka имеет распределенный модуль данных для обмена информацией в кластере:

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

Ответ 3

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

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

На данный момент мы решили, что каждый Сервис должен быть разделен на чистый и отделенный веб-сервис (RESTful), который выполняет реальную работу, и отдельный Connector-Service, который прослушивает шину и может также вызывать другие сервисы. Этот коннектор работает в фоновом режиме. Это только вызвано автобусными сообщениями. Затем он попытается добавить данные в основной сервис с помощью вызовов REST. Если служба отвечает с ошибкой согласованности, соединитель попытается исправить это, извлекая необходимые данные из вышестоящей службы и внедряя их по мере необходимости. (Мы не можем позволить пакетным заданиям "синхронизировать" данные в блоке, поэтому мы просто выбираем то, что нам нужно). Если есть лучшие идеи, мы всегда открыты, но "тянуть" или "просто изменить модель данных" - это не то, что мы считаем осуществимым...

Ответ 4

Думаю, здесь есть две основные силы:

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

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

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

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

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

Ответ 5

Я думаю, вы можете подойти к этой проблеме с двух сторон, совместной работы и моделирования данных:

Сотрудничество в сфере обслуживания

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

Моделирование данных

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

Ответ 6

"соответственно обновить связанные объекты в своих соответствующих базах данных" → дублирование данных → FAIL.

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

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

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

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