Ads Platform
B2B видеореклама VAST/VMAP. Миллионы показов/сутки. CPM-аукцион в реальном времени. Точный учёт бюджетов до копейки.
100M запросов/день< 50ms на VAST< 10ms аукционO(log N) операции
Бизнес-задача
Платформа связывает рекламодателей и паблишеров. Рекламодатель задаёт максимальный CPM и бюджет, паблишер — минимальный CPM. Платформа проводит аукцион, показывает рекламу, списывает деньги, начисляет выплаты — всё в реальном времени.
Ключевая задача
Запрос на рекламу должен вернуть VAST за < 50ms, но при этом нужно: проверить баланс, провести аукцион по CPM, отфильтровать по гео/формату/тегам, сгенерировать XML с трекинг-пикселями.
Решение: разделение на два сервиса. Java — админка, управление кампаниями, финансы. Go — горячий путь: аукцион, отдача VAST, приём событий. Горячие данные (кампании, балансы) — в Redis, Go читает только оттуда.
Архитектура
┌─────────────────┐ ┌─────────────────┐
│ Java Backend │────▶│ PostgreSQL │
│ (админка, API) │ │ (users, camps) │
└────────┬────────┘ └─────────────────┘
│ sync
┌────┴────┐
│ Redis │
└────┬────┘
│ read-only
┌────────┴────────┐ ┌─────────────────┐
│ Go Service │────▶│ NATS │
│ (аукцион, VAST) │ │ (events) │
└────────┬────────┘ └────────┬────────┘
│ │ batch
│ ┌────────┴────────┐
└─────────────▶│ ClickHouse │
└─────────────────┘Почему два сервиса
Админка и показ рекламы — разные паттерны нагрузки. Админка: сложные формы, транзакции, отчёты. Показ: тысячи RPS, задержка критична.
Java (Spring Boot) для админки — ORM, Security, сложная бизнес-логика. Go (fasthttp) для горячего пути — минимум аллокаций, горутины, < 10ms на запрос. Независимое масштабирование: Go-поды автоскейлятся по CPU.
Аукцион второй цены (Redis)
Активные кампании индексированы в Redis Sorted Set по CPM. При запросе: пересечение множеств (формат ∩ теги ∩ geo), сортировка по ставке, проверка баланса. Победитель платит ставку второго места + 1 цент (аукцион второй цены — стандарт AdTech). Всё за O(log N).
- •adv:active — множество активных кампаний
- •adv:cpm — отсортированное множество по ставке
- •adv:{id}:tags — теги кампании
- •balance:{member} — текущий баланс (атомарные операции)
Финансовый учёт: горячий кошелёк + холодный журнал
Баланс нужен мгновенно (проверка перед показом), но также нужен полный аудит транзакций.
Redis хранит текущий баланс — атомарный DECRBY при каждом показе, проверка за O(1). PostgreSQL хранит append-only журнал транзакций для финансовой отчётности, сверки и восстановления Redis после сбоя.
Фоновая задача сверяет балансы Redis с суммой событий в PostgreSQL, исправляет расхождения, алертит при аномалиях.
Пакетная обработка событий (NATS → ClickHouse)
100M запросов/день = 100M INSERT-ов. ClickHouse любит пакеты, не любит одиночные вставки.
Go публикует события в NATS без ожидания ответа. Отдельный потребитель накапливает буфер (1000 событий или 2 сек таймаут), затем пакетный INSERT. Нагрузка на ClickHouse снижена в 1000 раз.
Генерация VAST/VMAP
IAB-стандарт для видеоплееров. VAST — одиночный ролик. VMAP — несколько рекламных блоков (до, в середине). XML генерируется на лету с URL-ами для трекинга.
Путь без аллокаций: sync.Pool для буферов, быстрые шаблонизаторы вместо encoding/xml. Минимизация нагрузки на сборщик мусора при высоком RPS.
Технологии
Java Backend
Java 21Spring Boot 3Spring Data JPASpring SecurityLiquibase
Go Service
Go 1.25fasthttpgo-redisclickhouse-goNATSMaxMind GeoIP2
Data
PostgreSQLClickHouseRedisNATS JetStreamS3 (видео)
Infra
KubernetesArgoCDPrometheusNGINX IngressSealed Secrets
Моя роль
Архитектура с нуля, реализация обоих сервисов (Java + Go), настройка Kubernetes-инфраструктуры.
- Спроектировал двухсервисную архитектуру (админка vs горячий путь)
- Реализовал CPM-аукцион на Redis с задержкой < 10ms
- Реализовал гибридную финансовую модель (горячий кошелёк Redis + журнал PostgreSQL) с фоновой сверкой
- Настроил GitOps-пайплайн (ArgoCD + автообновление образов)