Масштабирование
Архитектура DataHUB рассчитана на широкий диапазон нагрузок — от десятков сообщений в сутки в небольшой инсталляции до сотен сообщений в секунду в крупных промышленных контурах. Статья описывает, как платформа масштабируется, где находятся пределы текущей архитектуры и какие изменения возможны при росте требований.
Текущая модель — вертикальная
В типовой поставке платформа разворачивается на одном сервере (или одной виртуальной машине), на котором запущены все блоки docker-compose. Это подходит для большинства промышленных нагрузок благодаря тому, что критичные компоненты (PostgreSQL, RabbitMQ, Spring Boot) сами по себе масштабируются вертикально очень хорошо.
Подбор ресурсов сервера — линейный по основным метрикам:
| Ресурс | На что влияет | Когда увеличивать |
|---|---|---|
| CPU | Скорость обработки REST-запросов, парсинг JSON, JWT | Растёт RPS на API, упирается в загрузку процессора backend |
| RAM | JVM-heap backend, кеши Caffeine, shared_buffers PostgreSQL, очереди RMQ | Растёт OOM, страдает throughput, наступает swap |
| Диск (IOPS) | PostgreSQL и RabbitMQ — оба критичны к IO | Растёт I/O wait, очереди RMQ застревают, БД отвечает медленно |
| Сеть | Только при больших объёмах файлового обмена | Большие inline-вложения (антипаттерн), мегатрафик с S3 |
Минимальные требования и рекомендации по железу — в Развертывание / Системные требования.
Где сходятся естественные пределы
В одноузловой схеме потолок упирается обычно в:
- Длина очередей RabbitMQ. Если consumer'ы клиентов читают медленнее, чем publisher'ы кладут, очереди растут — это не сбой, это нормальное поведение, но в какой-то момент диск кончится. Решение — оптимизация на стороне consumer'ов (масштабирование клиентских модулей) или ограничение скорости отправителей.
- Размер PostgreSQL. В текущей схеме БД хранит только конфигурацию обмена и пользователей LK — это десятки–сотни МБ даже на крупной инсталляции, потолок маловероятен. Если после выпуска централизованного трекинга сообщений БД начнёт расти за счёт лога — потребуется настроить политику архивации (это будет описано вместе с выпуском фичи).
- JVM heap backend. При большом количестве одновременных consumer'ов и больших сообщениях память растёт. Лечится — увеличением
-Xmxили вынесением части нагрузки.
В реальных эксплуатациях все эти пределы достигаются на разном железе при разных профилях нагрузки — универсальной формулы «после X сообщений нужно масштабировать вверх» нет.
Горизонтальные возможности
Архитектура backend'а допускает горизонтальное масштабирование в нескольких вариантах. Они не входят в типовую поставку, но возможны при необходимости.
Несколько экземпляров backend
Spring Boot stateless по дизайну — авторизация через JWT не хранит сессий, данные хранятся в БД и кешах. Несколько экземпляров backend за одним nginx — рабочая модель:
flowchart LR
N[nginx] --> B1[backend-1]
N --> B2[backend-2]
N --> B3[backend-3]
B1 & B2 & B3 --> PG[(PostgreSQL)]
B1 & B2 & B3 --> MQ[RabbitMQ]
Что нужно учесть:
- Кеши Caffeine локальны для каждого экземпляра. Инвалидация по событию работает в рамках одного процесса. При нескольких экземплярах нужен либо распределённый кеш (Redis, Hazelcast), либо TTL-only-модель (без явной инвалидации, с короткими TTL).
- RabbitMQSyncService. Несколько экземпляров будут параллельно пытаться синхронизировать топологию. Нужен либо leader election (через DB lock), либо распределение по zones. Сейчас работает на одном экземпляре по умолчанию.
- Регламентные задания. То же — нужны leader election или Quartz cluster.
В типовой поставке эти препятствия не решаются «из коробки»; для перехода на multi-backend нужна доработка.
Разделение LK и Exchange
LK-контур и Exchange-контур в текущей реализации делят один Spring Boot-процесс (см. Архитектура сервиса). При сильном дисбалансе нагрузки (Exchange сильно нагружен, LK — почти нет) их можно вынести в отдельные приложения с общей БД:
backend-exchange— только пакетыcom.datahub.exchange.*.backend-lk— толькоcom.datahub.backend.*.
Преимущества: независимое масштабирование, изоляция сбоев (упавший LK не валит обмен), разные SLA на разные домены. Недостатки: усложнение деплоя, два артефакта, общая БД остаётся узким местом.
Это изменение возможно без переделки модели данных — границы пакетов и сейчас чисто разделены.
RabbitMQ — кластеризация и quorum queues
Текущая поставка использует одиночный узел RabbitMQ с classic-очередями. Для повышения доступности возможны два пути:
Classic mirroring (deprecated в RMQ 3.x, удалено в 4.x). Зеркалирование классических очередей на несколько узлов кластера. Просто, но устарело.
Quorum queues. Очереди, основанные на алгоритме Raft, реплицируются на N узлов кластера, переживают потерю до (N-1)/2 узлов. Это современный путь HA для RabbitMQ. Требует:
- Кластер из минимум 3 узлов RMQ.
- Параметризации создания очередей в
exchange.rabbit.RabbitMQManagementClient(тип очереди + параметр репликации). - Перепланирования топологии — quorum queues имеют другие характеристики по производительности на одном узле.
Это изменение архитектурное, не входит в текущую поставку, но реализуемо при выпуске «HA-edition» платформы.
PostgreSQL — реплики и шардинг
Read-replicas (стандартная streaming replication PostgreSQL) могут разгрузить чтение лога доставки и админ-операций. Запись остаётся на primary. Требует доработки backend'а для маршрутизации запросов read/write — Spring Data JPA позволяет это через @Transactional(readOnly=true) и multiple datasource.
Шардинг (Citus, partitioning by tenant) — для очень крупных инсталляций с многими тенантами. В типовом сценарии не нужен; обсуждается отдельно при проектировании конкретного крупного развёртывания.
Кеши
Caffeine — локальный in-memory кеш. На одном экземпляре backend работает отлично; при переходе на multi-backend требует замены на распределённый (Redis, Hazelcast) — иначе инвалидация по событию между экземплярами не работает.
Текущая стратегия — TTL ~ 60 секунд + явная инвалидация. При переходе на TTL-only модель (без инвалидации) задержка между изменением настройки и применением может вырасти, но это допустимо для большинства сценариев.
Файловое хранилище
S3-совместимое хранилище по своей природе горизонтально масштабируемо — это сделано облачными провайдерами или MinIO в кластерном режиме. Backend и шина в файловом обмене не участвуют в горячем пути (см. Файловое хранилище) — отдают только ссылку. Это означает, что рост файлового трафика не нагружает основную инфраструктуру шины, а упирается только в характеристики S3.
Что НЕ масштабируется горизонтально из коробки
devops-core— один блок, создаёт сетьdh_network. Не масштабируется и не нужен в множественном числе.nginx— в типовой схеме один. Может быть несколько за внешним балансировщиком (HAProxy, облачный LB), но это уже отдельный сценарий.flyway— одноразовое задание, по определению однократно.pg-agmin— UI-инструмент, в множественном числе не нужен.
Метрики и сигналы для решения о масштабировании
| Сигнал | Что значит | Что делать |
|---|---|---|
| CPU backend > 70% устойчиво | Нагрузка на API упёрлась в CPU | Вертикально (больше CPU) или горизонтально (больше backend-инстансов) |
| RAM PostgreSQL близко к лимиту | shared_buffers + work_mem не помещаются | Вертикально (больше RAM) или тюнинг параметров postgresql.conf |
| Растущая длина очередей RMQ | Consumer'ы клиентов отстают | Не платформа, а клиенты — масштабируйте на их стороне |
| IO wait > 30% | Диск не справляется | SSD/NVMe, отдельный диск под PG, отдельный под RMQ |
| Длинная задержка между приёмом и доставкой | Любая из причин выше | По диагностике конкретной точки замедления (см. Диагностика / Производительность) |
Что дальше
- Развертывание / Системные требования — рекомендации по железу.
- Развертывание / Production-настройка — тонкая настройка PostgreSQL и других компонентов.
- Архитектура сервиса — почему монолитная компоновка остаётся базовой.
- Диагностика / Производительность — как находить узкие места в эксплуатации.