Марк Ричардс

Паттерны архитектуры

программного обеспечения

Общие шаблоны архитектуры и способы их применения


Исходный текст, 2015 / Русский перевод, 2023


Оглавление

ГЛАВА 2
Событийно-ориентированная архитектура
(Event-Driven Architecture)
Паттерн событийно-ориентированной архитектуры — это популярный распределённый асинхронный шаблон архитектуры, используемый для создания приложений с высокой степенью масштабируемости. Он также обладает высокой адаптивностью и может использоваться как для маленьких приложений, так и для больших, сложных приложений. Событийно-ориентированная архитектура состоит из очень независимых и высоко-специализированных компонентов обработки событий, которые асинхронно получают и обрабатывают события.

Паттерн событийно-ориентированной архитектуры состоит из двух основных топологий, в частности: топология посредника (mediator) и топология брокера (broker). Топология посредника обычно используется для оркестрации нескольких шагов в рамках события через центрального посредника, в то время как топология брокера используется, когда нужно связать события вместе без использования центрального посредника. Поскольку характеристики архитектуры и стратегии реализации различаются между этими двумя топологиями, важно понимать, какая из них лучше всего подходит для вашей конкретной ситуации.
Топология посредника
Топология посредника эффективна для событий, которые состоят из нескольких шагов и требуют определённого уровня оркестрации для обработки события. Например, одно событие по размещению биржевой сделки может потребовать, чтобы вы сначала провели валидацию сделки, а затем проверили эту сделку на соответствие правилам размещения биржевых сделок, назначили сделку брокеру, рассчитали комиссию, и, наконец, разместили сделку у этого брокера. Все эти шаги потребуют определённого уровня оркестрации, чтобы определить порядок выполнения этапов и того, какие из них могут выполняться последовательно, а какие параллельно.
В топологии посредника существует 4 основных типа компонентов архитектуры: очереди событий, посредник событий, каналы событий и обработчики событий. Поток событий начинается с того, что клиент отправляет событие в очередь событий (event queue), которая используется для его передачи посреднику событий. Посредник событий (event mediator) получает начальное событие и оркестрирует его, посылая дополнительные асинхронные события в каналы событий (event channels) для выполнения каждого этапа процесса. Обработчики событий (event processors), которые прослушивают каналы событий, получают событие от посредника событий и применяют определённую бизнес-логику для обработки события. Рисунок 2-1 показывает общую топологию посредника в паттерне событийно-ориентированной архитектуре.

Рисунок 2-1. Топология посредника в событийно-ориентированной архитектуре

В событийно-ориентированной архитектуре обычно встречается от десятка до нескольких сотен очередей событий. Паттерн не определяет способ реализации компонента очереди событий. Это может быть очередь сообщений, эндпоинт веб-сервиса или любая комбинация этих компонентов.
В этом паттерне существует два типа событий: начальное событие (initial event) и событие обработки (processing event). Начальное событие — это исходное событие, полученное посредником, в то время как события обработки — это события, которые создаются посредником и принимаются компонентами обработки событий.
Компонент посредника событий отвечает за оркестрацию шагов, содержащихся в начальном событии. Для каждого шага в начальном событии посредник событий посылает определённое событие обработки в канал событий, которое затем принимается и обрабатывается обработчиком событий. Важно отметить, что посредник событий не выполняет бизнес-логику, необходимую для обработки начального события. Скорее он знает шаги, необходимые для обработки начального события.
Каналы событий используются посредником событий для асинхронной передачи определённых событий обработки, связанных с каждым шагом начального события с помощью обработчика событий. Каналы событий могут быть либо очередями сообщений, либо топиками сообщений (message topics), хотя топики сообщений наиболее широко используются в топологии посредника, так, что события обработки могут обрабатываться несколькими обработчиками событий (каждый из которых выполняет свою задачу в зависимости от полученного события обработки).
Компоненты обработчика событий содержат бизнес-логику приложения, необходимую для процесса обработки события. Обработчики событий — это автономные, независимые, сильно разобщённые архитектурные компоненты, которые выполняют определённую задачу в приложении или системе. Хотя степень гранулярности компонента обработчика событий может быть как мелкой (например, расчёт налога с продаж по заказу), так и крупной (например, обработка страхового требования), важно помнить, что каждый компонент обработчика событий должен выполнять одну бизнес-задачу и не полагаться на других обработчиков событий для выполнения своей конкретной задачи.
Посредник событий может быть реализован различными способами. Как архитектор, вы должны понимать каждый из этих вариантов внедрения, чтобы убедиться, что выбранное вами решение для посредника событий соответствует вашим потребностям и требованиям.
Наиболее простой и распространённой реализацией посредника событий является использование центров интеграции (integration hub) с открытым исходным кодом. Например, Spring Integration, Apache Camel и Mule ESB. Потоки событий в этих интеграционных узлах с открытым исходным кодом обычно реализуются с помощью кода Java или DSL (domain-specific language). Для более сложного посредничества и оркестрации можно использовать BPEL (business process execution language) в сочетании с таким BPEL-движком с открытым исходным кодом, как Apache ODE. BPEL — это стандартный язык, основанный на XML, который описывает данные и шаги, необходимые для обработки начального события. Для очень больших приложений, требующих гораздо более сложной оркестрации (включая шаги взаимодействия с человеком), вы можете реализовать посредника событий с помощью менеджера бизнес-процессов BPM (business process manager), такого как jBPM .
Понимание ваших потребностей и их учёт для выбора правильной реализации посредника событий является критически важным для успеха любой событийно-ориентированной архитектуры, использующей эту топологию. Использование интеграционного узла с открытым исходным кодом для выполнения очень сложной оркестрации управления бизнес-процессами — это путь в провалу, как и реализация BPM решения для выполнения простой логики маршрутизации.
Для иллюстрации работы топологии посредника предположим, что вы застрахованы в страховой компании и решили переехать. В этом случае начальное событие может называться как событие переезда (relocation event). Шаги, связанные с обработкой события переезда (relocation event), содержатся в посреднике событий, как показано на Рисунке 2-2. Для каждого шага начального события посредник событий создаёт событие обработки (например, изменение адреса (change address), перерасчёт котировок (recalc quote)), отправляет это событие обработки в канал событий и ждёт, пока событие обработки будет реализовано соответствующим обработчиком событий (например, процесс клиента, процесс котировки). Этот процесс продолжается до тех пор, пока все этапы начального события нет будут обработаны. Одиночная полоса над шагами перерасчёта котировок и обновления претензии в посреднике событий указывает на то, что эти шаги могут выполняться одновременно.

Рисунок 2−2. Пример топологии посредника

Топология брокера
Топология брокера отличается от топологии посредника тем, что в ней нет центрального посредника событий. Скорее, поток сообщений распределяется между компонентами обработчика событий по цепочке через лёгкий брокер сообщений (например ActiveMQ, HornetQ и т.д.). Эта топология полезна, когда у вас относительно простой поток обработки событий и не хотите (или не нуждаетесь) в централизованной оркестрации событий.
В топологии брокера существует два основных типа архитектурных компонентов: компонент брокера (broker) и компонент обработчика событий (event processor). Компонент брокера может быть централизованным или распределённым и содержит все каналы событий, которые используются в потоке событий. В качестве каналов событий, содержащихся в компоненте брокера, могут выступать очереди сообщений, топики сообщений или их комбинации.
Эта топология показана на Рисунке 2-3. Как видно из диаграммы, в ней отсутствует центральный компонент посредника событий, который бы контролировал и реализовал оркестрацию начального события. В этом случае каждый компонент обработчика событий отвечает за обработку события и публикацию нового события, указывающего на только что выполненное действие. Например, обработчик событий, который балансирует портфель акций, может получить начальное событие под названием разделение акций (stock split). На основании этого начального события обработчик событий может выполнить перераспределение портфеля, а затем опубликовать новое событие брокеру под названием «rebalance portfolio», которое затем будет подхвачено другим обработчиком событий. Обратите внимание, что могут быть случаи, когда событие публикуется обработчиком событий, но не подхватывается другим обработчиком событий. Это часто бывает, когда вы развиваете приложение или предоставляете будущие функциональные возможности и расширения.

Рисунок 2-3. Топология брокера событийно-ориентированной архитектуры

Чтобы показать, как работает топология брокера мы будем использовать тот же пример, что и в топологии посредника (переезд застрахованного лица). Поскольку в топологии брокера отсутствует центральный посредник событий для получения начального события компонент «Customer Process» получает событие напрямую, изменяет адрес клиента и посылает событие о том, что он изменил адрес клиента (например, событие change address). В этом примере есть два обработчика событий, которые заинтересованы в событии изменения адреса (change address): «quote process» и «claims process». Компонент обработки тарифов (quote process) пересчитывает новые тарифы автострахования на основании изменения адреса и публикует событие для остальной части системы, указывающее на то, что он сделал (например, событие recalc quote). С другой стороны, компонент обработки страховых требований получает то же событие изменения адреса (change address), но в этом случае он обновляет непогашенную страховую претензию и публикует событие в системе как событие обновления претензии (update claim). Затем эти новые события подхватываются другими компонентами обработчика событий и цепочка событий продолжается в системе до тех пор, пока не будет больше опубликовано ни одного события для этого конкретного начального события.

Рисунок 2-4. Пример топологии брокера

Как видно из Рисунка 2-4, топология брокера — это цепочка событий для выполнения бизнес-функций. Лучший способ понять топологию брокера — это представить её в виде эстафеты. В эстафете бегуны держат эстафетную палочку и пробегают определённое расстояние, затем передают палочку следующему бегуну и так далее по цепочке, пока последний бегун не пересечёт финишную черту. В эстафете, как только бегун передаёт эстафетную палочку, он завершает забег. Это также справедливо и для топологии брокера. Как только обработчик событий передаёт событие он больше не вовлечён в обработку этого конкретного события.
Рекомендации
Паттерн событийно-ориентированной архитектуры является сложным паттерном для реализации, в основном из-за его асинхронной, распределённой природы. При реализации этого паттерна необходимо решить различные проблемы распределённой архитектуры, такие как: доступность дистанционных процессов, отсутствие отзывчивости и логика повторного подключения брокера, в случае сбоя брокера или посредника.
Одной из рекомендаций, которую необходимо учитывать при выборе этого паттерна архитектуры, является отсутствие атомарных транзакций для одного бизнес-процесса. Поскольку компоненты обработчика событий сильно разобщены и распределены, очень трудно поддерживать транзакции между ними. По этой причине при проектировании приложения с использованием этого паттерна необходимо постоянно думать о том, какие события могут выполняться самостоятельно, а какие нет, и соответствующим образом планировать гранулярность обработчиков событий. Если вы обнаружите, что вам нужно разделить один шаг процесса работы между обработчиками событий, то есть вы используете отдельные обработчики событий для чего-то, что должно быть неделимой транзакцией — вероятно, этот паттерн не подходит для вашего приложения.
Возможно, одним из самых сложных аспектов паттерна событийно-ориентированной архитектуры является создание, обслуживание и управление контрактами компонентов обработчиков событий. Каждое событие обычно имеет специальный контракт, связанный с ним (например, значения данных и формат данных, передаваемых обработчику событий). При использовании этого паттерна крайне важно с самого начала выбрать стандартный формат данных (например, XML, JSON, Java Object и т.д.) и установить политику управления версиями контрактов.
Анализ паттерна
Далее приведены оценки и общий анализ характеристик паттерна событийно-ориентированной архитектуры. Оценка для каждой из характеристик основана на естественном проявлении данной характеристики, как способности, качества (архитектуры), проявляющейся при реализации паттерна, а также на том, чем в целом известен этот паттерн. Для сопоставительного сравнения и получения представления о том, как этот паттерн соотносится с остальными паттернами нашего обзора, обратитесь к Приложению А в конце книги.
Общая адаптивность (agility)
Оценка: Высокая
Анализ: Общая адаптивность — это способность быстро реагировать на постоянно меняющуюся среду. Поскольку компоненты обработчика событий предназначены для одной цели и полностью отделены от других компонентов обработчика событий, изменения обычно касаются одного или нескольких обработчиков событий и могут быть быстро внесены без влияния на другие компоненты.
Простота развёртывания
Оценка: Высокая
Анализ: В целом этот паттерн легко поддаётся развёртыванию благодаря разделенным компонентам обработчика событий. Если сравнить топологию брокера и топологию посредника, то топология брокера легче поддаётся развёртыванию, потому что компонент посредника событий тесно связан с обработчиками событий. Изменение в компоненте обработчика событий может потребовать изменения в компоненте посредника событий, что требует развёртывания обоих компонентов для любого изменения.
Тестируемость
Оценка: Низкая
Анализ: Хотя модульное тестирование отдельных компонентов не является слишком сложным, оно требует специализированного клиента для тестирования или инструмента тестирования для генерации событий. Тестирование также осложняется асинхронной природой этого паттерна.
Производительность
Оценка: Высокая
Анализ: Безусловно, возможно реализовать событийно-ориентированную архитектуру, которая не работает хорошо из-за всей задействованной инфраструктуры обмена сообщениями, в целом, паттерн достигает высокой производительности благодаря асинхронным возможностям. Другими словами, возможность выполнения разобщённых, параллельных, асинхронных операций перевешивает стоимость организации и очистки очередей сообщений.
Масштабируемость
Оценка: Высокая
Анализ: Масштабируемость в этом паттерне естественным образом достигается с помощью высоко-независимых и разобщённых обработчиков событий. Каждый обработчик событий может быть масштабирован отдельно, что обеспечивает крупную масштабируемость.
Простота разработки
Оценка: Низкая
Анализ: Разработка может быть сложной из-за асинхронной природы паттерна, а также из-за создания контрактов и необходимости более продвинутой обработки ошибок в коде для не отвечающих обработчиков событий и вышедших из строя брокеров.

Глава 3
Микроядерная архитектура
(Microkernel architecture)