| api | ||
| assets | ||
| deployments | ||
| internal | ||
| scripts | ||
| .cursorignore | ||
| .dockerignore | ||
| .gitignore | ||
| .golangci.yml | ||
| docker-compose.yml | ||
| README.md | ||
| taskfile.yml | ||
Ads Platform
Введение
Ads Platform – EDA-платформа для управления рекламными кампаниями, статистикой, пользователями, рекламодателями и клиентами.
Ключевые сервисы:
- Adservice: показ рекламы, обработка кликов.
- Campaignservice: управление кампаниями.
- Statisticservice: сбор и анализ статистики.
- Userservice: управление пользователями, рекламодателями, клиентами и ML оценками.
Поддержка:
- RabbitMQ (сообщения)
- Postgres (основные данные)
- Redis (кэш)
- MongoDB (документы)
- Minio (файлы)
- Prometheus, Grafana, Tempo, Jaeger (мониторинг)
Схема
Запуск
Требования: Docker, Docker Compose
docker-compose up -d
docker-compose ps
Сервисы:
- Adservice: http - 7004, grpc - 6012
- Campaignservice: http - 7001, grpc - 6010
- Statisticservice: http - 7006, grpc - 6011
- Userservice: http - 7000, grpc - 6009
- Доп.: RabbitMQ (5672/15672), Prometheus (9090), Grafana (3000)...
Пути
Userservice:
POST /api/v1/advertisers/bulk– создать рекламодателейPOST /api/v1/clients/bulk– создать клиентов
Campaignservice:
POST /api/v1/advertisers/{advertiser_id}/campaigns– создать кампаниюPOST /api/v1/advertisers/{advertiser_id}/campaigns/image– кампания с изображениемPOST /api/v1/advertisers/{advertiser_id}/campaigns/text– кампания с текстомGET/PUT/DELETE /api/v1/advertisers/{advertiser_id}/campaigns/{campaign_id}– управление кампанией
Statisticservice:
GET /api/v1/stats/advertisers/{advertiser_id}/campaigns– статистика кампанийGET /api/v1/stats/advertisers/{advertiser_id}/campaigns/daily– ежедневная статистикаGET /api/v1/stats/campaigns/{campaign_id}– детальная статистикаGET /api/v1/stats/campaigns/{campaign_id}/daily– ежедневная детальная статистика
Adservice:
GET /api/v1/ads– получить рекламу (client_id)POST /api/v1/ads/{ad_id}/click– регистрировать кликPOST /api/v1/time/advance– имитация сдвига времени
Схема данных
Ниже представлены основные таблицы и связи между сущностями, используемые для хранения данных в нашей СУБД (PostgreSQL):
Таблица Clients (clients)
| Поле | Тип | Описание |
|---|---|---|
| client_id | UUID | Уникальный идентификатор клиента. Используется для однозначной идентификации пользователя. |
| login | VARCHAR | Имя пользователя или логин, используемый для доступа к сервисам. |
| age | INTEGER | Возраст клиента, используется для таргетинга рекламы. |
| gender | VARCHAR | Пол клиента (например, M/F), используется при таргетинге. |
| location | VARCHAR | Географическое местоположение клиента. |
| created_at | TIMESTAMP | Дата и время создания записи. |
Комментарий: Таблица Clients хранит базовую информацию о пользователях, взаимодействующих с платформой рекламы.
Таблица Advertisers (advertisers)
| Поле | Тип | Описание |
|---|---|---|
| advertiser_id | UUID | Уникальный идентификатор рекламодателя. |
| name | VARCHAR | Название или бренд рекламодателя. |
| created_at | TIMESTAMP | Дата создания рекламодателя. |
Комментарий: Таблица Advertisers содержит сведения о рекламодателях, которые запускают рекламные кампании. Один рекламодатель может иметь несколько кампаний.
Таблица Campaigns (campaigns)
| Поле | Тип | Описание |
|---|---|---|
| campaign_id | UUID | Уникальный идентификатор кампании. |
| advertiser_id | UUID | Внешний ключ, связывающий кампанию с таблицей Advertisers. |
| impressions_limit | INTEGER | Максимальное количество показов (impressions) для кампании. |
| clicks_limit | INTEGER | Максимальное количество кликов для кампании. |
| cost_per_impression | DECIMAL | Стоимость одного показа рекламы. |
| cost_per_click | DECIMAL | Стоимость одного клика по рекламе. |
| ad_title | VARCHAR | Заголовок рекламного объявления. |
| ad_text | TEXT | Текстовое содержание объявления. |
| start_date | TIMESTAMP или INT | Дата начала активности кампании. |
| end_date | TIMESTAMP или INT | Дата окончания активности кампании. |
| targeting | JSONB | Настройки таргетинга (например, возраст, пол, локация). |
| created_at | TIMESTAMP | Дата создания кампании. |
Комментарий: Таблица Campaigns отвечает за хранение информации о рекламных кампаниях. Каждый рекламодатель может иметь несколько кампаний, что реализуется через внешний ключ advertiser_id. Параметры impressions_limit и clicks_limit задают ограничения по показам и кликам, а cost_per_impression и cost_per_click определяют стоимость данных действий.
Таблица MLScores (ml_scores)
| Поле | Тип | Описание |
|---|---|---|
| ml_score_id | UUID | Уникальный идентификатор записи оценки. |
| advertiser_id | UUID | Внешний ключ, связывающий оценку с таблицей Advertisers. |
| client_id | UUID | Внешний ключ, связывающий оценку с таблицей Clients. |
| score | INTEGER | Значение оценки, определяющее релевантность рекламы для клиента. |
| created_at | TIMESTAMP | Дата создания записи оценки. |
Комментарий: Таблица MLScores используется для хранения оценок, рассчитанных с помощью машинного обучения. Эти оценки помогают ранжировать рекламные кампании и определять, насколько предложение соответствует интересам конкретного клиента.
Связи между таблицами
- Clients и MLScores: Один клиент может иметь несколько оценок (по каждому рекламодателю), что помогает персонализировать показ рекламы.
- Advertisers и Campaigns: Один рекламодатель может создавать множество кампаний, что реализуется через внешний ключ advertiser_id в таблице Campaigns.
- Advertisers и MLScores: Рекламодатель может иметь несколько оценок, связанных с различными клиентами.
API
- Userservice: управление рекламодателями, клиентами, ML оценками.
- Adservice: показ рекламы и клики.
- Campaignservice: создание и управление кампаниями.
- Statisticservice: предоставление статистики.
Выбор технологий
- Postgres: надежное хранение реляционных данных. (кто-то еще юзает MySQL?)
- Redis: быстрый кэш. Тупо база
- MongoDB: гибкое хранение документов. Для сервисов, где трафик преимущественно на чтение.
- RabbitMQ: асинхронные сообщения. быстрее для настройки чем Kafka.
- Minio: S3-хранилище для файлов. Удобно, S3-совместимое.
- Мониторинг: Prometheus, Grafana, Tempo, Jaeger. Тупо база чтобы быстро мониторить что за херня произошла у клиента прямо с телефончика пока сидишь в старбаксе.
Прогнозируемая нагрузка в продакшене
| Сервис | Эндпоинт | Запросов/час |
|---|---|---|
| Userservice | POST /api/v1/advertisers/bulk | 50–500 |
| Userservice | GET /api/v1/advertisers/{id} | 2500–5000 |
| Userservice | POST /api/v1/clients/bulk | 50–500 |
| Userservice | GET /api/v1/clients/{id} | 5000–15000 |
| Userservice | POST /api/v1/ml-scores | 1500–2500 |
| Adservice | GET /api/v1/ads | 25000–50000 |
| Adservice | POST /api/v1/ads/{ad_id}/click | 10000–25000 |
| Adservice | POST /api/v1/time/advance | 25–50 |
| Campaignservice | GET /api/v1/advertisers/{advertiser_id}/campaigns | 5000–25000 |
| Campaignservice | POST /api/v1/advertisers/{advertiser_id}/campaigns | 50–250 |
| Campaignservice | POST /api/v1/advertisers/{advertiser_id}/campaigns/image | 50–250 |
| Campaignservice | POST /api/v1/advertisers/{advertiser_id}/campaigns/text | 50–250 |
| Campaignservice | GET /api/v1/advertisers/{advertiser_id}/campaigns/{campaign_id} | 2500–10000 |
| Campaignservice | PUT /api/v1/advertisers/{advertiser_id}/campaigns/{campaign_id} | 50–250 |
| Campaignservice | DELETE /api/v1/advertisers/{advertiser_id}/campaigns/{campaign_id} | 25–100 |
| Statisticservice | GET /api/v1/stats/advertisers/{advertiser_id}/campaigns | 5000–15000 |
| Statisticservice | GET /api/v1/stats/advertisers/{advertiser_id}/campaigns/daily | 2500–7500 |
| Statisticservice | GET /api/v1/stats/campaigns/{campaign_id} | 2500–10000 |
| Statisticservice | GET /api/v1/stats/campaigns/{campaign_id}/daily | 1500–5000 |
Тестирование
Доступны unit, load и e2e тесты. Правда успел сделать только для userservice. Запуск тестов происходит через testcontainers.
task unit-test
task load-test
task e2e-test
Мониторинг
- Jaeger: http://localhost:16686
- Grafana: http://localhost:3000/goto/2SH8GUcNg?orgId=1
- Prometheus: http://localhost:9090
Алгоритм показа рекламы
При получении запроса на показ рекламы происходит следующий алгоритм:
-
Получение данных клиента:
- Сначала происходит попытка извлечения информации о клиенте из кеша (Redis).
- Если клиент не найден в кеше, данные ищутся в базе данных (MongoDB) и затем сохраняются в кеш для ускорения будущих запросов.
-
Получение активных рекламных кампаний:
- Производится фильтрация кампаний по критериям таргетинга: возраст, пол, локация, а также проверка дат начала и окончания кампании.
-
Получение ML оценок (ML Scores):
- Извлекаются оценки релевантности для рекламодателей, что используется для определения интереса клиента к рекламным объявлениям.
-
Ранжирование рекламных кампаний:
- Рекламные кампании анализируются и ранжируются с использованием Recommendation Engine.
- При расчёте рейтинга учитываются такие параметры, как ожидаемая прибыль, релевантность (на основе ML Score), уровень выполнения по импрессиям и кликам, а также фактор свежести кампании.
-
Выбор и отображение рекламы:
- Выбирается кампания с наивысшим рейтингом.
- Асинхронно обновляются статистические данные (увеличивается счётчик показов, impressions) в фоновом режиме.
- Формируется ответ, содержащий информацию о выбранном объявлении для отображения клиенту.
Ниже приведена блок-схема, иллюстрирующая данный алгоритм:
flowchart TD
A[Получение запроса на показ рекламы] --> B[Получить данные клиента]
B --> C{Клиент найден в Redis?}
C -- Да --> D[Использовать данные из Redis]
C -- Нет --> E[Поиск клиента в MongoDB]
E --> F[Сохранить данные в Redis]
D & F --> G[Получить активные кампании по таргетингу]
G --> H[Получить ML Scores для клиента]
H --> I[Ранжировать кампании в Recommendation Engine]
I --> J[Выбрать кампанию с наивысшим рейтингом]
J --> K[Асинхронное обновление статистики (impressions)]
K --> L[Формирование ответа с выбранным объявлением]
L --> M[Отправка ответа клиенту]
Также представлена диаграмма последовательности для основного запроса на показ рекламы:
sequenceDiagram
participant C as Клиент
participant AS as Ad Service
participant R as Redis
participant M as MongoDB
participant RM as Recommendation Engine
C->>AS: Запрос рекламы (client_id)
AS->>R: Получение данных клиента
alt Клиент найден
R-->>AS: Данные клиента
else Клиент не найден
AS->>M: Поиск клиента
M-->>AS: Данные клиента
AS->>R: Кэширование данных
end
AS->>M: Получение активных кампаний
AS->>M: Получение ML Scores
AS->>RM: Ранжирование кампаний
RM-->>AS: Выбранная кампания
AS->>AS: Асинхронное обновление статистики
AS-->>C: Отправка объявления
