Поделиться через


Реализация взаимодействия на основе событий между микрослужбами (события интеграции)

Подсказка

Это фрагмент из электронной книги «Архитектура микрослужб .NET для контейнеризованных приложений .NET», доступной в документации .NET или в виде бесплатного скачиваемого PDF-файла, который можно прочитать в автономном режиме.

Архитектура микросервисов .NET для приложений .NET в контейнерах, миниатюра обложки электронной книги.

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

События можно использовать для реализации бизнес-транзакций, охватывающих несколько служб, что дает вам конечную согласованность между этими службами. В конечном итоге согласованная транзакция состоит из ряда распределенных действий. При каждом действии микрослужба обновляет бизнес-сущность и публикует событие, которое активирует следующее действие. Помните, что транзакции не охватывают базовую сохраняемость и шину событий, поэтому идемпотентность должна быть обработана. На рисунке 6–18 ниже показано событие PriceUpdated, опубликованное через шину событий, поэтому обновление цен передается в корзину и другие микросервисы.

Схема асинхронного взаимодействия на основе событий при помощи шины событий.

Рис. 6-18. Взаимодействие, управляемое событиями с использованием шины событий

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

Использование брокеров сообщений и автобусов служб для производственных систем

Как отмечалось в разделе архитектуры, можно выбрать несколько технологий обмена сообщениями для реализации абстрактной шины событий. Но эти технологии находятся на разных уровнях. Например, RabbitMQ, брокер сообщений, находится на более низком уровне, чем коммерческие продукты, такие как Azure Service Bus, NServiceBus, MassTransit или Brighter. Большинство этих продуктов могут работать на основе RabbitMQ или служебной шины Azure. Выбор продукта зависит от количества функций и объема масштабируемости, необходимых для вашего приложения.

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

Если вам требуются высокоуровневые абстракции и более богатые функции, такие как Sagas для длительных процессов, которые упрощают распределенную разработку, другие коммерческие и открытые автобусы обслуживания, такие как NServiceBus, MassTransit и Brighter стоит оценить. В этом случае абстракции и API для использования обычно будут непосредственно теми, которые предоставляются этими высокоуровневыми служебными шинами вместо собственных абстракций (например, простыми абстракциями простой шины событий, предоставляемыми в eShopOnContainers). В этом случае можно изучить форк eShopOnContainers с помощью NServiceBus (дополнительный производный образец, реализованный Particular Software).

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

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

События интеграции

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

Событие интеграции в основном является классом хранения данных, как показано в следующем примере:

public class ProductPriceChangedIntegrationEvent : IntegrationEvent
{
    public int ProductId { get; private set; }
    public decimal NewPrice { get; private set; }
    public decimal OldPrice { get; private set; }

    public ProductPriceChangedIntegrationEvent(int productId, decimal newPrice,
        decimal oldPrice)
    {
        ProductId = productId;
        NewPrice = newPrice;
        OldPrice = oldPrice;
    }
}

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

Существует только несколько видов библиотек, которыми следует делиться между микрослужбами. Одним из них являются библиотеки, которые являются окончательными блоками приложений, такими как API клиента шины событий, как и в eShopOnContainers. Другой — это библиотеки, которые представляют собой средства, которые также могут использоваться как компоненты NuGet, такие как сериализаторы JSON.

Шина событий

Шина событий позволяет организовать взаимодействие по принципу публикации и подписки между микрослужбами без необходимости их явного знания друг о друге, как показано на рис. 6-19.

Схема, показывающая базовый шаблон публикации и подписки.

Рис. 6-19. Основы публикации и подписки на события в шине событий

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

Шаблон наблюдателя

В шаблоне Observer основной объект (известный как наблюдаемый) уведомляет другие интересующие объекты (известные как наблюдатели) с соответствующей информацией (событиями).

Шаблон публикации и подписки (pub/sub)

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

Посредник или шина событий

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

Шина событий обычно состоит из двух частей:

  • Абстракция или интерфейс.

  • Одна или несколько реализаций.

На рисунке 6-19 можно увидеть, как с точки зрения приложения шина событий является ничем иным, как каналом Pub/Sub. Способ реализации этого асинхронного взаимодействия может отличаться. Она может иметь несколько реализаций, чтобы можно было переключаться между ними в зависимости от требований среды (например, рабочей среды и сред разработки).

На рисунке 6–20 можно увидеть абстракцию шины событий с несколькими реализациями на основе технологий обмена сообщениями инфраструктуры, таких как RabbitMQ, служебная шина Azure или другой брокер событий и сообщений.

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

Рис. 6–20. Несколько реализаций шины событий

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

Определение интерфейса шины событий

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

public interface IEventBus
{
    void Publish(IntegrationEvent @event);

    void Subscribe<T, TH>()
        where T : IntegrationEvent
        where TH : IIntegrationEventHandler<T>;

    void SubscribeDynamic<TH>(string eventName)
        where TH : IDynamicIntegrationEventHandler;

    void UnsubscribeDynamic<TH>(string eventName)
        where TH : IDynamicIntegrationEventHandler;

    void Unsubscribe<T, TH>()
        where TH : IIntegrationEventHandler<T>
        where T : IntegrationEvent;
}

Метод Publish прост. Шина событий будет транслировать событие интеграции любой микрослужбе или даже внешнему приложению, подписавшемуся на это событие. Этот метод используется микрослужбой, публикующей событие.

Методы Subscribe (можно иметь несколько реализаций в зависимости от аргументов) используются микрослужбами, которые хотят получать события. Этот метод имеет два аргумента. Первое из событий для подписки — это событие интеграции (IntegrationEvent). Второй аргумент — это обработчик событий интеграции (или метод обратного вызова), IIntegrationEventHandler<T>который должен выполняться, когда микрослужба приемника получает это сообщение о событии интеграции.

Дополнительные ресурсы

Некоторые решения для обмена сообщениями, готовые к работе: