- 500 million DAU
- Каждый пользователь 150 раз запрашивает ленту
- Каждый пользователь 50 раз пишет посты в день
Расчеты
Просмотр ленты
- 500M × 150 = 75 000 000 000 запросов
- Средний Read RPS: 75B ÷ 86,400 сек = 868 056 RPS
- Пиковый Read RPS: 868 056 × 3 = 2 604 167 RPS
- 3 - примерное значение для peak multiplier. Главное не забудь проговорить про пиковые значения
Посты
- Постов в день создание: 500M × 50 = 25 000 000 000
- Средний Write RPS: 25,000,000,000 ÷ 86,400 сек = 289 352 RPS
- Пиковый Write RPS: 289 352 × 3 = 868 056 RPS
- 3 - примерное значение для peak multiplier. Главное не забудь проговорить про пиковые значения
Объем данных:
Размеры данных на ОДИН пост
Текстовые данные:
- Текст поста: 500 байт (средний размер)
- Метаданные: 200 байт
- Итого текстовых данных: 700 байт на пост
Медиа данные:
- 70% постов содержат фото (среднее 2 МБ)
- 30% постов содержат видео (среднее 10 МБ)
- Средний размер медиа: 4.4 МБ на пост
- 4.4 - примерное значение
Текстовые данные в день:
- 25 000 000 000 × 700 = 175 000 000 000 00 байт = 16,298 ГБ/день
Медиа данные в день:
- 25 000 000 000 × 4.4 МБ = 107 422 000 ГБ/день
ОБЩИЙ объем WRITE данных: 104,920 ТБ/день
Детальное описание архитектуры
| Компонент | Назначение | Технологии (предполагаемые) |
|---|---|---|
| GeoDNS, LB, API Gateway | Роутинг запросов | Nginx/HAProxy |
| Blob Storage | Хранение фотографий | AWS S3, Google Cloud Storage |
| SQL Database (PG) | Метаданные юзеров (id, user_id, пути) | PostgreSQL с шардированием по id (по сути это user_id) |
| NoSQL Database (Mongo) | Метаданные постов (id, user_id, пути) | Mongo с шардированием по post_id |
| image-processor | Пережатие фотографий | Асинхронные воркеры (RabbitMQ/Kafka) |
| Post-Creator Service | Посты | |
| Subscribers Service | Управление подписками | |
| Feed Cache | Кеширование ленты. Отдельный для celebrity и обычных пользователей | Redis (шардированный по user_id) |
| Feed Worker | Вычитка из Topic и определение: celeb или нет | |
| Feed Service | Лента | |
| Worker | Отвечает за мерж ленты |
Схема взаимодействий
Сценарий 1: Публикация поста
- Точка входа: POST
v1/post → API Gateway → PostService - Последовательность:
- Фото загружается в Blob Storage.
- Метаданные сохраняются в SQL DB.
- image-processor асинхронно генерирует уменьшенные версии.
Сценарий 2: Добавление подписки
- Точка входа: POST
v1/subscribe → API Gateway → Subscribers Service - Последовательность:
- Запись в SQL DB (статус pending).
- Инвалидация кеша ленты при подтверждении подписки.
Сценарий 3: Получение ленты
- Точка входа: GET
v1/feed → API Gateway → Feed Service - Обработка ошибок: Fallback на другие workers с ограничением времени (timeout 400 мс).
Давай подробно разберем варианты хранения постов для ленты
Вариант 1: Запрос подписок из SQL DB (сервис subscribers) → сбор последних постов из cache → мерж → отдача. То есть тут мы храним все посты для всех пользаков. Этот вариант смотрим в данной версии.
- Плюс: меньше походов в другие сервисы и выше скорость
- Минус: намного больше RAM
Вариант 2: Хранить в cache только для активных пользаков, а для неактивных ходить в сервис subscribers (чтобы получить подписки), затем идти в сервис posts (чтобы по ID выцепить все посты). А еще при падении Feed делает redirect на сервис Post и выгребать ленту оттуда. Этот вариант разберем в ver 3
- Плюс: меньше RAM
- Минус: больше логики и потенциальных задержек
Более мягкая альтернатива: перед сервисом feed можно поставить cache и сначала делать запросы туда. То есть полная лента для 10 постов у каждого юзера. А уже дальше по алгоритму в Вариант 1 или Вариант 2