Событийная архитектура превращается из ограниченной модели взаимодействия нескольких сервисов в широкую сеть производителей, потребителей и каналов обмена сообщениями. На этом этапе основная проблема уже не заключается в отправке сообщения, а состоит в том, чтобы знать, что именно отправляется, кто этим владеет, от чего это зависит и как изменить его схему или восстановить инфраструктуру, не вызвав производственные сбои. Этой теме посвящена сессия Ian Cooper об управлении асинхронными API в большом масштабе, основанная на практическом инженерном опыте Just Eat Takeaway.
Почему ситуация усложняется при масштабировании?
В небольших организациях участники команд могут знать, какие сервисы публикуют и потребляют события, а темы или каналы можно создавать через фреймворк обмена сообщениями либо по прямому запросу к команде платформы. Однако с увеличением числа команд и сервисов такой подход теряет эффективность. Может быть неясно, кто владеет определённой конечной точкой, какой контракт описывает сообщение или существуют ли неизвестные потребители в командах данных и аналитики.
Риск возникает, когда команда изменяет схему сообщения после консультации только с известными потребителями, тогда как другие команды зависят от этого сообщения, не входя в круг общения. Удаление темы, которая считается неиспользуемой, может привести к потере критически важного пути, особенно если нет надёжного способа узнать её владельца или повторно развернуть необходимую инфраструктуру иначе, чем заново развернув значительные части системы.
Три направления управления событийными API
Cooper разделяет проблему на обнаружение, управление и предоставление ресурсов. Всё начинается с понимания того, что следует описывать в любой конечной точке, через так называемые «ABC»: адрес, привязку и контракт. Адрес определяет место потока сообщений, привязка описывает протокол, транспорт и кодирование, а контракт определяет заголовки и данные, которые несёт сообщение.
В этом контексте AsyncAPI предоставляет аналог механизмов документирования HTTP-интерфейсов, таких как OpenAPI, моделируя серверы, каналы, сообщения, операции и привязки, относящиеся к протоколу. Она поддерживает описание сообщений с использованием JSON Schema, Avro и Protobuf, а также позволяет повторно использовать определения вместо их дублирования в нескольких файлах. Однако Cooper подчёркивает, что размещение сотен файлов AsyncAPI в одном репозитории само по себе не решает проблему обнаружения: при увеличении хранилища событий текстовый поиск остаётся ограниченным.
Поэтому организациям могут потребоваться такие инструменты каталогизации, как EventCatalog или Marmot, либо открытый реестр, например xRegistry, чтобы отображать потоки сообщений и связи между ними, а также связывать схемы с производителями и потребителями. Эти инструменты различаются уровнем визуализации и готовыми интерфейсами, но их общая функция заключается в переносе обнаружения из вопроса, задаваемого в Slack, или поиска в разрозненных репозиториях в область доступного для запросов сервиса.
Роль CloudEvents в унификации метаданных
CloudEvents решает другую часть проблемы, предоставляя унифицированный набор метаданных, таких как идентификатор, источник, версия и тип, а также необязательные поля для типа содержимого данных, темы, времени и ссылки на схему. Тип события можно использовать для маршрутизации сообщения или выбора механизма его десериализации, когда несколько типов совместно используют один канал.
В сессии объясняется, что режимы binary и structured в CloudEvents связаны со способностью транспортного протокола переносить заголовки, а не с тем, является ли сообщение «двоичным» или «структурированным» в привычном смысле. Важность этой детали особенно заметна в таких системах, как SNS, где ограниченное число атрибутов сообщения может быстро потребляться, если все данные CloudEvents размещены в заголовках, что делает упаковку этих данных в тело практичным вариантом в некоторых сценариях.
Управление не ограничивается документацией
Документирование контракта объясняет потребителям, что представляет собой сообщение, но не препятствует производителю опубликовать изменение, нарушающее их зависимости. Поэтому представленный подход рекомендует использовать реестр схем, применяющий правила совместимости при обновлении контрактов и отклоняющий сообщения или изменения, которые не соответствуют утверждённым правилам.
Безопасные практики обычно включают добавление полей в качестве необязательных, отсрочку удаления полей до тех пор, пока все потребители не перестанут их использовать, а также рассмотрение переименования как изменения, состоящего из удаления и добавления. Изменение типа поля обычно считается нарушающим совместимость изменением. Его можно выполнить посредством добавления нового поля с требуемым типом, последующего перевода потребителей на него и удаления старого поля.
Что меняется на практике?
Главный вывод для технического читателя заключается в том, что управление событиями в большом масштабе требует взаимосвязанной цепочки, а не отдельного инструмента: AsyncAPI для описания интерфейсов, CloudEvents для унификации метаданных, каталог или реестр для упрощения обнаружения, реестр схем для обеспечения совместимости, а также автоматизация для предоставления инфраструктуры и контроля её дрейфа. Без этих уровней событийная архитектура может превратиться в сеть невидимых зависимостей.
При этом сохраняются практические ограничения: одни стандарты не обеспечивают полного знания о потребителях, а инструменты различаются поддержкой интерфейсов, визуализации и интеграции с брокерами. Поэтому применение этого подхода требует чёткого определения владельцев контрактов и каналов, связывания изменений с процессами CI/CD и сохранения пути восстановления, способного заново создать ресурсы вместо зависимости от личных знаний или устаревшей документации.