Обработка изменений в управляемой событиями архитектуре микросервиса

9

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

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

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

В этом случае различные сервисы в архитектуре должны иметь «контракты» относительно содержания этих событий (атрибутов и т. Д.). Так что сервисы имеют «слабо связанные зависимости» от этих событий

Мой вопрос: как мы можем справиться с изменениями в этих событиях?

Итак, скажем, служба A регистрирует новых пользователей в приложении. Поэтому он отправляет событие «UserRegistered». Служба B получает это событие и обрабатывает его. Но некоторые разработчики из команды службы C решили, что им также нужен пол зарегистрированного пользователя. Таким образом, событие изменилось и пол атрибута добавляется в событие «UserRegistered».

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

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

CGeense
источник
В каком формате ваши сообщения, или это то, что вы можете разработать? Некоторые форматы сообщений допускают необязательные атрибуты. В зависимости от реализации устройства чтения, вы можете добавлять дополнительные атрибуты без необходимости обновления всех устройств чтения.
Томас Оуэнс
Я свободен в выборе формата для моих сообщений. Я думаю, что использование JSON - лучший путь. Важно, что эти разные сервисы построены на разных языках. Вот почему необходим общий формат, такой как XML или JSON.
CGeense

Ответы:

1

События не о том, что изменилось. Они о том, когда что-то изменилось.

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

Это не решает проблему передачи этих изменений. Это просто мешает ему стать частью системы событий.

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

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

candied_orange
источник
Не сильно ли это увеличит трафик между сервисами? Используя пример в вопросе, UserRegisteredсобытие, если было событие, которое не содержало информацию о пользователе, было бы 1 опубликованное сообщение на шину и затем {количество заинтересованных служб} запросов к службе пользователя или опубликованные сообщения в автобус. Затем будут сообщения {количество заинтересованных служб} разного размера. Хотя я думаю, что это, вероятно, более чистый дизайн на бумаге, если производительность вызывает беспокойство, она ломается в любой нетривиальной системе, особенно в сети.
Томас Оуэнс
@ThomasOwens Отправка данных с событием означает, что если у меня N наблюдателей, я отправляю N сообщений. Отправка одного события означает, что я отправляю 3N сообщений, только одно из которых имеет пакет данных. Это прекрасно масштабируется даже по сети. Единственный существенный недостаток - это утроение вашего отставания. Не говорю, что вы не можете найти более оптимальное решение для конкретной ситуации. Я демонстрирую, что системы событий и версии данных не должны быть связаны.
Candied_Orange
1
Вся идея этого автобуса состоит в том, чтобы отделить различные сервисы. Используя часть промежуточного программного обеспечения, мы можем убедиться, что эти сервисы не знают друг друга и могут существовать и общаться, не зная о существовании друг друга. Если мы удалим состояние из этих событий и позволим сервисам соединяться друг с другом напрямую, мы соединяем эти сервисы. Таким образом, мы никогда не сможем повторно развернуть один сервис без необходимости
повторного развертывания
1
Дело в том, что система событий вам не нужна расширяемые данные. JSON или XML делают это хорошо, если вы не собираетесь изменять имена или структуры, которые были ранее установлены. Я сделал то же самое с коллекциями. Система событий не должна заботиться о полах. Если он отправляет данные, он должен просто передать их, и что-то на другом конце будет либо заботиться о полах, либо нет.
candied_orange
1

Фреймворки, такие как NServiceBus, обрабатывают это, используя управление версиями событий с полиморфной отправкой сообщений.

Например, версия 1 службы A может опубликовать событие как IUserRegistered_v1. Когда Service A версии 1.1 необходимо включить дополнительное поле, оно может объявить интерфейс IUserRegistered_v1_1, который будет наследоваться от IUserRegistered_v1, а также объявить некоторые дополнительные поля.

Когда служба A публикует событие IUserRegistered_v1_1, NServiceBus отправляет сообщение всем конечным точкам, которые обрабатывают IUserRegistered_v1 или IUserRegistered_v1_1.

pnschofield
источник
0

Постепенное улучшение

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

Скала

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

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

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

JimmyJames
источник
-1

@CandiedOrange делает правильный комментарий в комментарии к своему собственному ответу относительно расширяемых форматов данных, таких как xml.

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

Вам нужно только обновить услуги, которые касаются - в данном случае - пола. Анализатор xml / json должен иметь возможность игнорировать дополнительные данные для других служб. Конечно, это зависит от вашего выбора парсера и формата данных события.

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

Сандер Верденбург
источник
Я хочу сказать, что он должен обрабатывать все виды изменений. Подумайте об услуге, которая транслирует событие. И это событие содержит свойство, которое устарело и должно быть удалено. Даже если эти другие службы не используют собственность, она сломает их просто потому, что они ожидают этого. Поэтому я прочитал эту статью на martinfowler.com о потребительских контрактах: martinfowler.com/articles/consumerDrivenContracts.html При применении этого принципа. Каждый провайдер (событие) знает, что от него ожидается. с помощью этой информации он может подтвердить, если он нарушает какие-либо потребители.
CGeense