Нейминг топиков Kafka
1. Общая схема именования топиков
Компонент | Описание | Пример |
---|---|---|
{env} |
Среда. Определяет окружение (dev, stage, prod). Самый высокоуровневый разделитель. | prod |
{system-code}.{system-name} |
Идентификатор системы. Уникальный код и имя системы-владельца топика. Определяет команду, ответственную за контракт данных. | 1234.geo |
{scope} |
Область видимости. public для взаимодействия с внешним миром, internal для коммуникации между микросервисами внутри системы. |
public |
{intent} |
Намерение. Тип сообщения: commands для директив на выполнение действия, events для уведомлений о свершившихся фактах. |
commands |
[{client-system-name}] |
Опционально. Только для public топиков с командами, обработка которых задействует API с ограниченным ресурсом (Resource-Constrained). Для internal не применяется. Уникальный системный идентификатор клиента (например, crm , billing , mobile-app ). Аналог {system-name} . |
abcd |
[{common}] ] |
Опционально. Только для public топиков с командами, обработка которых задействует API с неограниченным ресурсом (Unconstrained). |
common |
{domain} |
Бизнес-домен. Соответствует одному из ключевых сервисов системы (geocoding , isochrone , vrp и т.д.). |
vrp |
{entity} |
Бизнес-сущность. Основной объект, над которым выполняется действие. Не должен дублировать домен. Как правило, это существительное, описывающее тип задачи (calculation , job , solution , query ). |
solution |
{action} |
Действие. Глагол в повелительном наклонении для команд (solve ) или в форме причастия прошедшего времени для событий (solved ). |
solve |
[.v{N}] |
(Опционально) Версия схемы. Суффикс версии для топиков с несовместимым изменением схемы (.v2 , .v3 ). |
.v2 |
[.suffix] |
(Опционально) Служебный суффикс. Для служебных топиков (.retry , .dlq ). |
.dlq |
Примечание насчет [.v{N}]
- Когда использовать: Только при выпуске несовместимой версии схемы сообщения (например, v2).
- Процесс внедрения:
- Создается новый топик
...calculated.v2
. - Развертываются новые потребители, которые подписываются и на старый (
...calculated
), и на новый (... calculated.v2
) топики. - Развертывается новый производитель, который начинает публиковать сообщения в
...calculated.v2
. - После того как все сообщения из старого топика обработаны, и все производители обновлены, подписку на старый топик можно убирать, а сам топик со временем архивировать.
- Создается новый топик
- Преимущество: Этот подход делает несовместимые изменения явными и безопасными. Он предотвращает "тихие"
сбои, когда потребитель просто игнорирует сообщение нового формата. Для совместимых изменений по-прежнему
используется заголовок
version
.
2. Примеры имен
В таблице ниже приведены примеры имен топиков для различных сценариев, иллюстрирующие применение гибридной стратегии и общих правил именования.
Сценарий / Сервис | Тип сообщения | Имя топика | Комментарий |
---|---|---|---|
Публичные топики: С ограниченным ресурсом (Изолированные топики) | |||
Геокодирование (клиент crm ) |
Команда | prod.1234.geo.public.commands.crm.geocoding.address.process |
Клиент crm отправляет команду на геокодирование. Используется персональный топик для контроля квот. |
Событие (Успех) | prod.1234.geo.public.events.crm.geocoding.address.processed |
Система публикует результат геокодирования в персональный топик клиента crm . |
|
Событие (Ошибка) | prod.1234.geo.public.events.crm.geocoding.address.failed |
Система уведомляет клиента crm о невосстановимой ошибке при обработке команды. |
|
Геопоиск (клиент real-estate-portal ) |
Команда | prod.1234.geo.public.commands.real-estate-portal.geosearch.query.process |
Клиент real-estate-portal ищет организации. Изолированный топик для управления лимитами. |
Событие (Успех) | prod.1234.geo.public.events.real-estate-portal.geosearch.query.processed |
Результат поиска организаций публикуется в персональный топик клиента. | |
Построение маршрута (клиент taxi-aggregator ) |
Команда | prod.1234.geo.public.commands.taxi-aggregator.routing.calculation.calculate |
Клиент taxi-aggregator запрашивает построение простого маршрута A->B. |
Событие (Успех) | prod.1234.geo.public.events.taxi-aggregator.routing.calculation.calculated |
Система публикует построенный маршрут в персональный топик клиента. | |
Расчет изохроны (клиент logistics-platform ) |
Команда | prod.1234.geo.public.commands.logistics-platform.isochrone.calculation.calculate |
Клиент logistics-platform запрашивает построение зоны доступности. |
Событие (Принято) | prod.1234.geo.public.events.logistics-platform.isochrone.calculation.accepted |
Система быстро подтверждает, что команда принята в обработку, для асинхронного ответа. | |
Событие (Успех) | prod.1234.geo.public.events.logistics-platform.isochrone.calculation.calculated |
Система публикует готовую изохрону в персональный топик клиента logistics-platform . |
|
Решение VRP (клиент delivery-service ) |
Команда | prod.1234.geo.public.commands.delivery-service.vrp.solution.solve |
Клиент delivery-service запрашивает решение задачи маршрутизации для автопарка. |
Событие (Успех) | prod.1234.geo.public.events.delivery-service.vrp.solution.solved |
Система публикует готовое решение VRP в персональный топик клиента delivery-service . |
|
Матрица расстояний (клиент analytics-dept ) |
Команда | prod.1234.geo.public.commands.analytics-dept.distancematrix.job.solve |
Клиент analytics-dept отправляет "тяжелую" задачу на расчет матрицы расстояний. |
Событие (Успех) | prod.1234.geo.public.events.analytics-dept.distancematrix.job.solved |
Система публикует результат расчета матрицы в персональный топик клиента analytics-dept . |
|
Публичные топики: С неограниченным ресурсом (Общие топики) | |||
Геосаджест (Общий) | Команда | prod.1234.geo.public.commands.common.geosuggest.query.process |
Любой клиент (crm , mobile-app и др.) отправляет запрос на подсказки в общий топик. |
Событие (Успех) | prod.1234.geo.public.events.common.geosuggest.query.processed |
Результаты для всех клиентов публикуются в общий топик. Клиенты фильтруют сообщения по correlation-id . |
|
Пространственные вычисления (Общий) | Команда | prod.1234.geo.public.commands.common.geospatial.calculation.calculate |
Любой клиент отправляет запрос на проверку пересечения координат с полигоном в общий топик. |
Событие (Успех) | prod.1234.geo.public.events.common.geospatial.calculation.calculated |
Результаты вычислений публикуются в общий топик для всех клиентов. | |
Обогащение данных (Общий) | Команда | prod.1234.geo.public.commands.common.enrichment.geodata.enrich |
Любой клиент запрашивает обогащение своих геоданных (например, добавление индекса). |
Событие (Успех) | prod.1234.geo.public.events.common.enrichment.geodata.enriched |
Результаты обогащения для всех клиентов публикуются в общий топик. | |
Внутренние топики (Internal Communication) | |||
Хореография сервисов | Команда | prod.1234.geo.internal.commands.enrichment.geodata.enrich |
Сервис geocoding после успешной обработки адреса инициирует его обогащение, отправляя команду сервису enrichment . |
Событие (Успех) | prod.1234.geo.internal.events.enrichment.geodata.enriched |
Сервис enrichment сообщает другим внутренним сервисам, что геоданные были обогащены. |
|
Пространственные вычисления | Команда | prod.1234.geo.internal.commands.geospatial.calculation.calculate |
Сервис VRP просит сервис пространственных вычислений проверить попадание точек в запрещенные геозоны. |
Событие (Успех) | prod.1234.geo.internal.events.geospatial.calculation.calculated |
Сервис пространственных вычислений публикует результат для внутреннего потребителя (сервиса VRP). | |
Управление конфигурацией | Команда | prod.1234.geo.internal.commands.system.configuration.update |
Команда на обновление конфигурации одного из сервисов (например, лимитов внешнего API). |
Событие (Успех) | prod.1234.geo.internal.events.system.configuration.updated |
Событие о том, что конфигурация была успешно обновлена и сервисы могут ее перечитать. | |
Служебные топики (Utility) | |||
DLQ (Изолированный public) | Команда | prod.1234.geo.public.commands.crm.vrp.solution.solve.dlq |
Команды от клиента crm , которые не удалось обработать после всех попыток. Требуют ручного разбора. |
DLQ (Общий public) | Команда | prod.1234.geo.public.commands.common.geosuggest.query.process.dlq |
Команды из общего топика, которые не удалось обработать. Помогает изолировать сбойные сообщения. |
DLQ (Internal) | Событие | prod.1234.geo.internal.events.geospatial.calculation.calculated.dlq |
Внутренние события, которые не смог обработать сервис-потребитель (например, сервис VRP). |
Retry (Общий public) | Команда | prod.1234.geo.public.commands.common.geosuggest.query.process.retry |
Топик для повторной обработки команд на геосаджест с экспоненциальной задержкой. |
Retry (Internal) | Команда | prod.1234.geo.internal.commands.enrichment.geodata.enrich.retry |
Топик для повторной обработки внутренних команд, если зависимый сервис временно недоступен. |
Эволюция схемы (Schema Evolution) | |||
Несовместимое изменение (Изолированный) | Событие | prod.1234.geo.public.events.crm.isochrone.calculation.calculated.v2 |
Топик для новой, несовместимой версии (v2) события о расчете изохроны для клиента crm . |
Несовместимое изменение (Общий) | Событие | prod.1234.geo.public.events.common.geosuggest.query.processed.v2 |
Топик для новой, несовместимой версии (v2) события геосаджеста. |
Несовместимое изменение (Команда) | Команда | prod.1234.geo.public.commands.crm.geocoding.address.process.v2 |
Топик для новой, несовместимой версии (v2) команды на геокодирование. |
3. Преимущества схемы именования топиков
Категория преимущества | Описание |
---|---|
1. Порядок и Предсказуемость | Структура имени топика является самодокументируемой. Любой инженер (разработчик, DevOps, аналитик) может по одному только имени топика понять его назначение: среда, система-владелец, публичный или внутренний, команда или событие, бизнес-домен и выполняемое действие. Это кардинально снижает когнитивную нагрузку и время на погружение в проект. |
2. Масштабируемость и Управляемость | Решение проблемы "взрыва топиков" с помощью гибридной стратегии. Сокращение количества топиков более чем в два раза снижает операционные издержки на их создание, поддержку и настройку ACL, что особенно критично при ручном управлении через UI. |
Гранулярный контроль над ресурсами. Изоляция топиков для API с ограниченным ресурсом (Resource-Constrained ) позволяет управлять квотами и лимитами на уровне каждого клиента. Можно остановить потребление сообщений от одного клиента, исчерпавшего лимит, не затрагивая работу остальных. |
|
Упрощение делегирования. Четкие и формализованные правила позволяют безопасно делегировать задачи по проектированию и созданию топиков системным аналитикам и DevOps-инженерам, высвобождая время архитектора для решения более высокоуровневых задач. | |
3. Надежность и Отказоустойчивость | Безопасная эволюция API. Подход с версионированием (.vN в имени топика для несовместимых изменений и заголовок version для совместимых) обеспечивает плавное и безопасное развитие контрактов без риска поломать существующих потребителей и без необходимости одновременного релиза всех сервисов. |
Стандартизация обработки ошибок. Единые суффиксы .retry и .dlq позволяют строить унифицированные механизмы мониторинга и алертинга. Появление сообщения в любом .dlq топике является однозначным сигналом о сбое, требующем внимания. |
|
Четкая зона ответственности. Префикс {system-code}.{system-name} однозначно закрепляет владение топиком за конкретной системой и командой, что исключает конфликты имен в общем кластере Kafka и упрощает поиск ответственных. |
|
4. Простота Интеграции и Поддержки | Явное разделение контрактов. Сегмент {scope} (public /internal ) четко разграничивает внутреннюю "кухню" системы и ее публичный API. Внешним системам предоставляется доступ только к public -топикам, что повышает безопасность и упрощает документирование интеграций. |
Прозрачность потоков данных. Информативные имена Consumer Group ({consumer-service-name}.{purpose} ) позволяют мгновенно определить, какой микросервис и с какой целью потребляет данные из топика. Это неоценимо при отладке проблем с производительностью, задержками (lag) или некорректной обработкой сообщений. |
|
Симметричность и логичность. Структура именования команд и событий симметрична (...commands...solve -> ...events...solved ). Это делает потоки данных интуитивно понятными и предсказуемыми как для внутренних, так и для внешних разработчиков. |
4. Недостатки схемы именования топиков и пути их устранения
Недостаток | Описание | Способы устранения недостатков |
---|---|---|
Избыточная длина (многословие) | Имена топиков получаются очень длинными. Это может быть неудобно при ручном вводе, а также может плохо отображаться в некоторых инструментах мониторинга и управления Kafka, где ширина колонок ограничена. | 1. Использовать аббревиатуры: Ввести и задокументировать в глоссарии стандартные сокращения (cmd для commands , evt для events , calc для calculate ). 2. Автоматизировать работу с именами: Разработать внутренние инструменты (CLI, плагины для IDE) или библиотеки, которые абстрагируют полные имена и позволяют разработчикам работать с короткими псевдонимами. 3. Настроить мониторинг: Использовать в инструментах мониторинга (Grafana) псевдонимы (aliases) на основе регулярных выражений для сокращения отображаемых имен. |
Жесткость и строгие рамки | Схема очень строгая. Если возникает новая бизнес-задача, которая плохо укладывается в утвержденную структуру {domain}.{entity}.{action} , это может вызвать трудности. Потребуется либо расширять глоссарий (что может быть бюрократизировано), либо использовать не совсем подходящие термины. |
1. Упростить обновление глоссария: Внедрить гибкий процесс обновления глоссария, например, через Pull Request'ы в репозиторий с документацией, с быстрым ревью от архитекторов или тимлидов. 2. Ввести общие термины: Добавить в глоссарий четко определенные "запасные" термины (например, task для {entity} , execute для {action} ) для уникальных или экспериментальных задач с обязательством последующего рефакторинга. 3. Проводить регулярные ревью: Организовать периодические встречи для анализа использования схемы, выявления новых паттернов и проактивного обновления глоссария. |
Сложность управления ACL | Поскольку ACL настраиваются по полному имени топика, а не по маске, большое количество топиков порождает огромное количество правил ACL. Например, расчет нового типа гео-задачи потребует создания как минимум 4-х топиков (...accepted , ...calculated , ...failed и основной топик команды), а также их retry и dlq вариаций. Это приведет к необходимости создавать десятки отдельных запросов на ACL, что замедляет разработку и увеличивает операционную нагрузку. |
1. Автоматизировать управление ACL: Управлять правилами ACL через Infrastructure as Code (Terraform, Ansible) и интегрировать этот процесс в CI/CD пайплайны. Заявка на новый топик автоматически создает и применяет нужные ACL. 2. Использовать доменные Service Accounts: Создавать сервисные аккаунты на уровне бизнес-домена (например, geo-vrp-producers , geo-vrp-consumers ), а не для каждого микросервиса, чтобы группировать права доступа. 3. Лоббирование использования префиксных ACL. Несмотря на текущие ограничения, стоит аргументированно донести до администраторов Kafka преимущества префиксных правил. Структура имени идеально для этого подходит ( prod.1234.geo.public.commands.* ). Это самый эффективный способ решения проблемы в долгосрочной перспективе. |
"Взрыв" количества топиков | Детальная сегментация (особенно по {action} и опционально по .vN и .dlq ) приводит к большому числу топиков. Например, одна бизнес-операция может порождать топики для команды, для событий accepted , processed /solved , failed , а также их dlq -версии. |
1. Консолидировать статусные события: Вместо отдельных топиков ...accepted , ...solved , ...failed использовать один общий топик событий (например, ...vrp.solution.status ), а конкретный тип события передавать в заголовке или теле сообщения. Это компромисс, который значительно сокращает число топиков и ACL. 2. Централизовать DLQ: Вместо создания .dlq для каждого основного топика, отправлять все "мертвые" сообщения в несколько общих DLQ-топиков (например, prod.1234.geo.internal.dead-letter-queue ), указывая исходный топик и причину ошибки в заголовках. |
"Взрыв топиков" при несовместимых изменениях схемы | Подход с суффиксом .vN для несовместимых изменений является безопасным, но приводит к накоплению большого количества топиков с разными версиями (...calculated , ...calculated.v2 , ...calculated.v3 ). Со временем это загромождает брокер, усложняет навигацию и требует постоянного хранения старых, потенциально уже не используемых топиков. |
1. Внедрение строгой политики жизненного цикла топиков. Формализовать и автоматизировать процесс архивации и удаления версионных топиков. Например: "Топик *.vN подлежит удалению через 90 дней после того, как в него перестали поступать сообщения и все команды-потребители подтвердили миграцию на *.v(N+1) ". 2. Использование одного топика с маршрутизацией по версии в заголовке. В качестве альтернативы можно использовать один топик, но реализовать в потребителях более сложную логику. Потребитель сначала читает только заголовки. Если заголовок version указывает на несовместимую версию, которую он не может обработать, он не пытается десериализовать тело, а сразу отправляет сообщение в свой персональный DLQ. Этот подход сложнее в реализации, но предотвращает "взрыв топиков". |
Высокая стоимость рефакторинга | Глубокая привязка имени топика к бизнес-структуре (domain , entity ) делает рефакторинг очень дорогим. Если будет принято решение переименовать домен или разделить его на несколько, это потребует массового переименования топиков и скоординированного обновления всех производителей и потребителей. |
1. Внедрить слой абстракции: Создать централизованную библиотеку или сервис конфигурации, который поставляет имена топиков приложениям. Разработчики запрашивают топик по логическому имени (topicProvider.get("vrpSolveCommand") ), а не хардкодят его. Рефакторинг сводится к изменению маппинга в одном месте. 2. Использовать паттерн "Strangler Fig": При рефакторинге создавать новые топики и временный сервис-роутер, который читает из старого топика и перенаправляет сообщения в новые. Это позволяет мигрировать клиентов постепенно и безболезненно. |
Зависимость от глоссария | Эффективность схемы напрямую зависит от актуальности и полноты глоссария. Если глоссарий не поддерживается или разработчики его игнорируют, система быстро потеряет консистентность, и главное преимущество — предсказуемость — будет утрачено. | 1. Интегрировать линтеры в CI/CD: Создать скрипты, которые на этапе сборки или Pull Request'а проверяют все используемые в коде и конфигурациях имена топиков на соответствие глоссарию (хранящемуся в machine-readable формате, например, YAML). 2. Использовать кодогенерацию: Генерировать классы с константами или файлы конфигурации с именами топиков напрямую из глоссария. Это исключает опечатки и гарантирует использование утвержденных имен. 3. Назначить владельцев: Четко определить ответственных за ведение глоссария и сделать его максимально доступным и удобным для всех разработчиков (например, как часть портала для разработчиков). |
Жесткость разделения public и internal |
Если событие изначально было создано как internal , а затем потребовалось выставить его наружу, это потребует создания нового public топика и, возможно, сервиса-"моста", который будет перекладывать сообщения из одного топика в другой. Это создает дополнительную работу и усложняет архитектуру. |
1. Уточнение семантики {scope} . Определить scope не только как видимость, но и как гарантию стабильности контракта. public означает стабильный, документированный API с обратной совместимостью. internal — контракт может меняться без строгого версионирования. 2. Принцип "Public по умолчанию". Принять соглашение, что все события по умолчанию создаются в public скоупе, если нет веских причин (например, соображения безопасности, передача чувствительных данных, крайне нестабильный внутренний контракт) делать их internal . Это минимизирует будущие миграции. |
6. Факторы, влияющие на разработку схемы именования
6.1. Сравнительный анализ стратегий "Общий топик" vs "Изолированный топик"
Стратегия "Изолированные топики для каждого клиента" предполагает, что для каждой внешней системы-клиента и для каждой команды, которую она может отправить, создается свой собственный, выделенный топик.
Критерий | Стратегия 1: Общий топик (1 на N клиентов) | Стратегия 2: Изолированный топик (1 на 1 клиента) | Рекомендация |
---|---|---|---|
Контроль лимитов (RPS/сутки) | Очень сложно. Требуется сложная логика в коде консьюмера для отслеживания client-id из заголовка, подсчета запросов в Redis и троттлинга. Возникают проблемы с конкуренцией за ресурсы между инстансами консьюмера. |
Просто и надежно. Лимиты можно контролировать на уровне консьюмера, выделяя ему определенное количество ресурсов (подов Kubernetes), или даже на уровне квот Kafka (если применимо). Каждый клиент обрабатывается независимо. | Изолированный топик |
Изоляция и надежность ("Шумный сосед") | Низкая. Клиент с высоким RPS (или клиент, отправляющий "тяжелые" запросы) может забить очередь, замедляя обработку для всех остальных клиентов. Сбойный клиент влияет на всех. | Высокая. Проблемы одного клиента (всплеск трафика, невалидные сообщения) изолированы в его собственном топике и не влияют на других. | Изолированный топик |
Масштабируемость | Ограниченная. Масштабирование консьюмера применяется ко всему потоку сообщений. Невозможно независимо масштабировать обработку для одного "крупного" клиента, не затрагивая "мелких". | Гибкая. Можно гибко выделять ресурсы. Клиенту с RPS 100 можно выделить больше подов-консьюмеров, чем клиенту с RPS 10. Масштабирование гранулярное и предсказуемое. | Изолированный топик |
Безопасность и ACL | Рискованно. Всем клиентам дается доступ к одному и тому же ресурсу. Компрометация ключей одного клиента может потенциально повлиять на весь поток команд. | Гранулярно и безопасно. Каждому клиенту выдается ACL только на его личный топик. В случае инцидента можно точечно отозвать доступ, не затрагивая работу других систем. | Изолированный топик |
Операционная сложность | Низкая на старте. Меньше топиков для создания и управления. | Выше. Требуется создавать новые топики и настраивать ACL для каждого нового клиента. Это приводит к "взрыву топиков". | Общий топик |
Простота поддержки и мониторинга | Сложно. Трудно отследить проблемы конкретного клиента в общем потоке. Метрики (лаг консьюмер-группы, количество сообщений) будут общими, что маскирует индивидуальные проблемы. | Просто. Легко мониторить производительность для каждого клиента отдельно (лаг, глубина очереди). Ошибки в DLQ сразу указывают на проблемного клиента. | Изолированный топик |
6.2. Категории публичных API
Все публичные API делятся на две категории:
- API с ограниченным ресурсом (Resource-Constrained): Команды, обработка которых требует вызова внешних платных API с лимитами (RPS, дневные квоты). Например,
geocoding
,distancematrix
. - API с неограниченным ресурсом (Unconstrained): Команды, обработка которых задействует только внутренние ресурсы системы, не имеющие жестких внешних ограничений. Например,
geosuggest
,geospatial
(если он работает на собственных данных),enrichment
.
6.3. Заголовки сообщений Kafka
Для сообщений Kafka устанавливаются следующие заголовки:
message-id
— уникальный идентификатор сообщения для отслеживания и дедупликации;correlation-id
— уникальный идентификатор цепочки взаимодействий;created-at
— при необходимости, позволит отслеживать RoundTripLatency;client-id
— идентификатор сервиса-отправителя;content-type
— формат тела сообщения, напримерapplication.vnd.apache.avro+binary
;version
— версия схемы сообщения:- Потребитель может подписаться на один топик (
...isochrone.isochrone.calculated
) и реализовать внутри себя логику обработки разных версий (например, через паттерн "Стратегия" или простой if/switch по значению заголовкаversion
). - Старые потребители, которые не умеют обрабатывать
v2
, просто проигнорируют такие сообщения (или отправят в DLQ), не сломавшись. Новые потребители смогут обрабатывать иv1
, иv2
.
- Потребитель может подписаться на один топик (
retry-count
— число попыток обработки (для топиков Retry и DLQ).
6.4. Решение проблемы "взрыва топиков" (Topic Explosion)
Проблема "взрыва топиков" возникает из-за стратегии создания выделенного набора топиков для каждой внешней системы-клиента ({client-system-name}
). При планируемом количестве интеграций (~50) и сервисов (~11) общее число публичных топиков может вырасти до 50 * 11 * 2 = 1100
(клиенты * домены * [команда + событие]), не считая служебных (.dlq
, .retry
) и версионных (.v2
) топиков. Это создает значительные операционные издержки, особенно при ручном управлении через UI и настройке ACL по полным именам.
Для решения этой проблемы можно внедрить Гибридную стратегию изоляции топиков, основанную на классификации API системы.
6.4.1. Принцип гибридной стратегии
Основной принцип — разделить все публичные API на две категории и применять к ним разные подходы к созданию топиков:
- API с ограниченным ресурсом (Resource-Constrained): Для команд, обработка которых требует контроля квот и лимитов (RPS, дневные лимиты). Для них сохраняется стратегия изолированных топиков на клиента. Это обеспечивает гранулярный контроль и предотвращает влияние одного клиента на другого.
- API с неограниченным ресурсом (Unconstrained): Для команд, обработка которых не связана с внешними платными лимитами. Для них используется стратегия общих (shared) топиков. Все клиенты отправляют команды в один общий топик для данного типа команды и получают события из одного общего топика для данного типа события.
6.4.2 Классификация сервисов
Разделим существующие сервисы системы на две категории в соответствии с этим принципом.
Категория | Домены ({domain} ) |
Обоснование |
---|---|---|
С ограниченным ресурсом | geocoding , geosearch , distancematrix , vrp , isochrone , routing |
Эти операции часто зависят от внешних платных API, являются ресурсоемкими или требуют строгого контроля квот на уровне каждого клиента. Изоляция здесь критична. |
С неограниченным ресурсом | geosuggest , geospatial , enrichment , notification , system |
Эти операции, как правило, используют внутренние ресурсы (базы данных, индексы), для которых индивидуальные лимиты на клиента не требуются или могут быть реализованы на уровне приложения. |
6.4.3. Обновленная схема именования
Для реализации гибридной стратегии вводится статическое значение common
в сегменте имени топика, который ранее предназначался для {client-system-name}
.
Тип API | Шаблон именования | Пример |
---|---|---|
С ограниченным ресурсом | ...public.{intent}.{client-system-name}.{domain}.{entity}.{action} |
prod.1234.geo.public.commands.crm.geocoding.address.process |
С неограниченным ресурсом | ...public.{intent}.common.{domain}.{entity}.{action} |
prod.1234.geo.public.commands.common.geosuggest.query.process |
6.4.5. Механизм работы
6.4.5.1. Потоки для API с ограниченным ресурсом
- Отправка команды: Клиент
crm
отправляет команду на геокодирование в свой персональный топик:...commands.crm.geocoding.address.process
. - Контроль лимитов: Система Геоаналитики имеет отдельную consumer group для топика
...commands.crm...
. Если клиентcrm
исчерпал свою квоту, система может остановить чтение из этого топика (остановить consumer), не затрагивая обработку команд от других клиентов. - Отправка события: Результат (успешный или ошибочный) публикуется в персональный топик событий клиента:
...events.crm.geocoding.address.processed
. Это гарантирует полную изоляцию.
6.4.5.2. Потоки для API с неограниченным ресурсом
- Отправка команды: Все клиенты (
crm
,mobile-app
,billing
) отправляют команды на геосаджест в один общий топик:...commands.common.geosuggest.query.process
. - Обработка: Система читает команды из общего топика единой consumer group.
- Отправка события: Результаты для всех клиентов публикуются также в один общий топик:
...events.common.geosuggest.query.processed
. - Фильтрация на стороне клиента: Клиент, подписанный на общий топик событий, обязан реализовать логику фильтрации, чтобы получать только "свои" ответы. Для этого он должен использовать заголовок
correlation-id
, который он сам установил в исходной команде. Система Геоаналитики обязана транслировать этот заголовок из сообщения с командой в сообщение с событием.
6.4.6. Оценка сокращения количества топиков
Проведем сравнительный расчет для 50 клиентов и 11 доменов.
Подход | Расчет | Итого (публичных топиков) |
---|---|---|
Полная изоляция (старый подход) | 50 клиентов * 11 доменов * 2 (cmd+evt) |
~1100 |
Гибридная стратегия (новый подход) | (50 клиентов * 5 доменов с огр. ресурсом * 2) + (1 * 6 доменов с неогр. ресурсом * 2) |
500 + 12 = ~512 |
Вывод: Гибридная стратегия позволяет сократить количество публичных топиков более чем в 2 раза, решая проблему "взрыва топиков" без ущерба для управляемости и надежности критичных сервисов.
6.4.7. Риски и компромиссы
Аспект | Описание риска/компромисса | Способ митигации |
---|---|---|
Сложность клиента | Клиенты, потребляющие события из common топиков, должны реализовывать логику фильтрации по correlation-id . |
Четко документировать этот контракт в спецификации API. Предоставлять примеры кода или библиотеки для клиентов. |
"Шумный сосед" в common топиках |
Ошибочная логика в одном из клиентов может привести к лавине некорректных сообщений в общем топике команд, что может замедлить обработку для всех. | Реализовать на входе системы (в consumer'ах common топиков) валидацию схемы и базовых правил. Использовать метрики для мониторинга количества сообщений в разрезе каждого клиента (по заголовку client-id ). |
Мониторинг | Мониторинг производительности в разрезе клиентов для common топиков становится сложнее, так как метрики Kafka (lag, throughput) агрегированы по топику. |
Использовать метрики на уровне приложения. Например, consumer должен публиковать в Prometheus метрики обработки сообщений с меткой (label) client_id , извлеченной из заголовка сообщения. |