Постановка задачи
📹 Что такое комментарии к прямым трансляциям?
Комментарии к прямым трансляциям - это функция, которая позволяет зрителям оставлять комментарии под видео в прямом эфире. Зрители видят непрерывный поток комментариев практически в реальном времени.
Функциональные требования
Основные требования
- Зрители могут оставлять комментарии к прямой трансляции.
- Зрители видят новые комментарии в реальном времени во время просмотра видео.
- Зрители могут просматривать комментарии, оставленные до их подключения к трансляции.
За рамками задачи
- Ответы на комментарии (треды).
- Реакции на комментарии.
Нефункциональные требования
Основные требования
- Система масштабируется до миллионов одновременных зрителей и поддерживает тысячи комментариев в секунду для каждого видео.
- Доступность важнее согласованности. Нас устраивает согласованность в конечном счете.
- Низкая задержка: комментарии доставляются практически в реальном времени. При нормальном качестве сети задержка от отправки до отображения на клиенте должна быть менее 200 мс.
За рамками задачи
- Безопасность и авторизация: комментировать могут только авторизованные пользователи.
- Модерация контента: фильтрация спама и недопустимых высказываний.
Человек воспринимает отклик как мгновенный, если задержка не превышает 200 мс. В интерфейсах задержка менее 200 мс кажется незаметной, поэтому для систем реального времени это ключевой целевой показатель.
На доске требования к системе могут выглядеть так:
Подготовка
Планирование подхода
Прежде чем проектировать систему, важно продумать стратегию. Для продуктовых задач план прост: мы последовательно выстраиваем архитектуру, по очереди закрывая каждое функциональное требование. Это помогает сохранять фокус и не увязнуть в деталях на ранних этапах. Затем мы переходим к нефункциональным требованиям, чтобы добавить в решение глубину и сложность, там, где это необходимо.
Основные сущности
Начнем с общего обзора основных сущностей системы. Они направят наши рассуждения и заложат основу для проектирования API.
Почему на этом этапе мы определяем только сущности, а не полную схему базы данных? Проектирование только началось, поэтому еще рано перечислять все колонки и типы полей. Сначала мы выделяем ключевые сущности, а затем дополняем модель данных по мере развития архитектуры.
В нашей задаче три основные сущности:
- User (Пользователь): зритель или автор трансляции.
- Live Video (Прямой эфир): видеопоток, который транслирует пользователь. Управляется отдельным видеосервисом, но важен для интеграции с нашей системой.
- Comment (Комментарий): текстовое сообщение, которое пользователь оставляет во время прямого эфира.
Теперь перейдем к проектированию API, последовательно разбирая каждое функциональное требование.
Проектирование API
Для создания комментария нужен простой POST-эндпоинт:
POST /comments/:liveVideoId
Header: Authorization: Bearer <JWT> | SessionToken
{
"message": "Отличное видео!"
}Обратите внимание: userId не передается в теле запроса. Он извлекается на
сервере из токена сессии или JWT, переданного в заголовке
авторизации. Это стандартный паттерн безопасности: передача userId в теле
запроса позволила бы злоумышленнику подменить идентификатор и отправить
комментарий от чужого имени.
Также нам понадобится эндпоинт для получения ранее опубликованных комментариев к выбранной прямой трансляции:
GET /comments/:liveVideoId?cursor={last_comment_id}&pageSize=10&sort=descДля этого эндпоинта важно разбиение на страницы. Мы подробно обсудим его при дальнейшем проектировании.
Высокоуровневый дизайн
Начнем проектирование с реализации первого функционального требования.
1. Зрители могут оставлять комментарии к прямому эфиру
Сначала реализуем отправку комментариев.
Это простая операция. Клиент отправляет POST-запрос с текстом сообщения на
эндпоинт POST /comments/:liveVideoId. Сервер валидирует запрос и сохраняет
комментарий в базу данных.
- Клиент автора комментария: веб- или мобильное приложение, позволяющее публиковать комментарии. Отвечает за аутентификацию пользователя и отправку сообщений в сервис комментариев.
- Сервис комментариев: принимает комментарии от клиентов, валидирует их и сохраняет в базу данных. Позже этот же сервис будет читать комментарии из базы и отдавать их зрителям.
- База данных: в качестве хранилища мы выбираем DynamoDB. Это быстрая, масштабируемая база данных с высокой доступностью. Она отлично подходит для простых комментариев, не требующих сложных связей и ACID-транзакций. Впрочем, здесь подошли бы и реляционные базы данных, такие как Postgres или MySQL.
Пошаговый процесс публикации комментария выглядит так:
- Пользователь вводит текст комментария на своем устройстве.
- Клиент отправляет комментарий в сервис комментариев через эндпоинт
POST /comments/:liveVideoId. - Сервис комментариев принимает запрос и сохраняет запись в базе данных.
С публикацией мы разобрались. Просмотр комментариев устроен сложнее.
2. Зрители видят новые комментарии во время просмотра прямой трансляции
После создания комментариев нужно организовать их рассылку зрителям. Когда один пользователь публикует комментарий, его должны сразу увидеть все остальные зрители трансляции.
Начнем с самого простого подхода - периодического опроса (polling).
Клиенты запрашивают новые комментарии каждые несколько секунд через эндпоинт
GET /comments/:liveVideoId?since={last_comment_id}. Параметр since указывает
идентификатор последнего комментария, который уже отображен у клиента. Сервер
возвращает все комментарии, опубликованные после него, а клиент добавляет их в
ленту на экране.
Такое решение не масштабируется. По мере роста числа комментариев и зрителей частоту опроса придется увеличивать. Это создаст огромную нагрузку на базу данных и приведет к множеству бесполезных запросов, поскольку в большинстве ответов новых комментариев не будет. Чтобы показывать комментарии с задержкой менее 200 мс, клиентам пришлось бы опрашивать базу каждые несколько миллисекунд, что нереалистично на практике.
Если на интервью вы уже знаете оптимальное решение, можно сразу перейти к нему, обосновав выбор и объяснив компромиссы.
Если задача встретилась впервые, полезно начать с простого решения. Оно создаст основу, которую можно улучшить во время детального погружения.
3. Зрители видят комментарии, оставленные до их подключения
При подключении к прямой трансляции пользователю нужны две вещи:
- Начать получать новые комментарии в реальном времени.
- Загрузить историю комментариев, опубликованных до подключения.
Пользователь должен иметь возможность прокручивать список вверх и постепенно подгружать более старые комментарии. Такой паттерн интерфейса называется бесконечной прокруткой и часто используется в приложениях для обмена сообщениями.
Первый набор недавних комментариев мы получаем через эндпоинт GET /comments/:liveVideoId. Параметр since здесь не подходит, так как он
возвращает комментарии новее заданной отметки времени, а для истории нужно
обратное: получить N последних комментариев, опубликованных до определенного
момента.
Для этого мы используем разбиение на страницы или пагинацию. Она разбивает большой объем данных на небольшие порции и работает в связке с бесконечной прокруткой.
Есть два основных подхода к пагинации: пагинация по смещению (offset pagination) и пагинация по курсору (cursor pagination).
Пагинация по курсору лучше подходит для нашей задачи: она не требует сканирования всех предыдущих строк, устойчива к появлению новых комментариев и отлично ложится на запросы по ключам в DynamoDB.
Детальные погружения
1. Как пересылать комментарии зрителям в реальном времени?
Периодический опрос был хорошим стартом, но для реальной системы этого недостаточно. Вместо того чтобы клиент пытался угадать момент появления комментариев и постоянно опрашивал сервер, используем модель отправки данных от сервера (server push). Тогда сервер сможет передавать новый комментарий клиенту сразу после публикации.
Есть два основных варианта: WebSockets и Server-Sent Events (SSE). Сравним их преимущества и недостатки.
Теперь поток обработки выглядит так:
- Пользователь публикует комментарий, и сервис комментариев сохраняет его в базе данных.
- Сервис отправляет комментарий через SSE всем клиентам, подписанным на эту прямую трансляцию.
- Клиент получает комментарий и отображает его в ленте.
Внимательный читатель заметит, что в таком виде решение все еще не масштабируется на миллионы пользователей. Разберем горизонтальное масштабирование в следующем разделе.
2. Как поддерживать миллионы одновременных зрителей?
Мы выбрали SSE и теперь должны его масштабировать. Для каждого зрителя необходимо держать открытое соединение. Современный сервер способен поддерживать большое число соединений (часто около 100 000 и более), однако раньше этого предела закончатся оперативная память, ресурсы процессора или файловые дескрипторы. Один сервер не сможет обслужить миллионы одновременных зрителей. Поэтому систему необходимо масштабировать горизонтально.
Разделим задачу масштабирования на две части:
- Координация серверов: если зрители одной трансляции подключены к разным серверам, как эти серверы узнают о новых комментариях?
- Сверхпопулярные трансляции: как справиться с нагрузкой, когда одно видео смотрят миллионы зрителей, а комментарии добавляются тысячами в секунду?
Вопреки распространенному заблуждению, сервер не ограничен 65 535 соединениями. Это число относится к диапазону номеров портов, а не к числу соединений, которые может обслуживать один порт сервера. Каждое TCP-соединение определяется сочетанием четырех параметров: IP-адрес источника, порт источника, IP-адрес назначения и порт назначения. При правильной настройке операционной системы и достаточных ресурсах один серверный порт способен поддерживать сотни тысяч одновременных SSE-соединений.
Координация серверов
Сначала разберемся с основной проблемой горизонтального масштабирования. После добавления серверов зрители одной прямой трансляции оказываются подключены к разным машинам:
- пользователь A смотрит Трансляцию 1 и подключен к Серверу 1;
- пользователь B смотрит ту же Трансляцию 1, но подключен к Серверу 2.
Когда под Трансляцией 1 появляется новый комментарий, запрос на публикацию приходит, например, на Сервер 1. Он легко доставит комментарий пользователю A, но не может напрямую передать его пользователю B на Сервер 2. Нам нужно сделать так, чтобы новый комментарий получили все зрители независимо от того, к какому серверу они подключены.
На интервью стоит отдельно упомянуть компромиссы разных брокеров сообщений. Kafka отлично масштабируется и обеспечивает высокую надежность, но тяжеловесна при динамическом создании и удалении подписок. Redis Pub/Sub обеспечивает минимальную задержку и легко справляется с динамическими подписками, что идеально подходит для нашей задачи. Отсутствие гарантий рассылки (fire-and-forget) в Redis Pub/Sub для нас не критично, так как все комментарии в любом случае сохраняются в базе данных, а пропуски компенсируются механизмом восстановления на клиенте.
И шардированный Pub/Sub с L7-балансировкой, и выделенный сервис диспетчеризации эффективно решают задачу координации. Модель с Pub/Sub и L7-маршрутизацией содержит меньше нестандартных компонентов, поэтому на интервью она предпочтительнее.
Сверхпопулярные трансляции
Описанные выше схемы координации отлично работают для большинства прямых эфиров. Но что делать, если начинается трансляция финала чемпионата мира, которую одновременно смотрят десятки миллионов человек, а комментарии поступают со скоростью 5 000 сообщений в секунду?
Это принципиально другой режим работы. При скорости 5 000 комментариев в секунду пользователь получает текстовый объем целой книги менее чем за полминуты. Если на экране смартфона помещается 20 строк, каждый комментарий будет виден не более 4 мс, после чего его вытеснят новые. Физически прочитать такой поток невозможно.
При подобной нагрузке классические цели систем реального времени (гарантированная доставка каждого сообщения и задержка до 200 мс) теряют практический смысл. Пользователь не пытается прочесть каждое отдельное сообщение, а лишь воспринимает общую эмоциональную атмосферу эфира.
Для подавляющего большинства прямых трансляций отлично подходит шардированный Pub/Sub с L7-балансировкой. Для сверхпопулярных трансляций наилучшую масштабируемость дает раздача периодических снимков через CDN. На интервью предложение перехода на CDN демонстрирует мышление уровня Staff, так как вы не просто масштабируете архитектуру в лоб, а переосмысливаете сами требования под реальные физические ограничения.
3. Как обрабатывать разрывы соединения и не терять комментарии?
Мобильные сети нестабильны: пользователи переключаются между Wi-Fi и сотовой связью, заходят в лифты и тоннели или сворачивают приложение. Надежная система должна бесшовно восстанавливать соединение и догружать пропущенные комментарии.
На интервью стандартного механизма Last-Event-ID достаточно для базового
ответа, но для того, чтобы оставить сильное впечатление, важно раскрыть детали:
ограничение глубины восстановления по времени, экономию батареи на мобильных
клиентах и дедупликацию данных на стороне приложения.
Итоговая архитектура
Объединив все рассмотренные компоненты, получаем итоговую архитектуру системы:
Что ожидается от кандидатов разных уровней?
Мы разобрали много всего. Какую часть этих тем необходимо продемонстрировать на реальном интервью? Рассмотрим ожидания для каждого уровня.
Middle
Широта и глубина знаний. От кандидата уровня Middle ожидается в первую очередь широта кругозора (примерно 80% широты и 20% глубины). Главная задача - построить рабочую высокоуровневую архитектуру, закрывающую основные функциональные требования. Многие узлы могут оставаться абстракциями, которые вы обсудили на общем уровне.
Проверка основ. Интервьюер будет проверять понимание базовых концепций и роли каждого компонента в системе. Например, после добавления API Gateway он может спросить, какие задачи решает этот шлюз и как устроена маршрутизация.
Самостоятельность и наводящие вопросы. Кандидат должен уверенно вести начальные этапы интервью. Ошибки и упущения в масштабируемости на поздних этапах допустимы: интервьюер может направлять ход рассуждений вопросами и подсказками.
Задача о Live-комментариях. Кандидат должен самостоятельно увидеть неэффективность периодического опроса и предложить переход к server push (SSE или WebSockets). При минимальных подсказках интервьюера - применить Pub/Sub для координации серверов.
Senior
Глубина знаний. Для уровня Senior фокус смещается в сторону технических деталей (примерно 60% широты и 40% глубины). Кандидат должен уверенно погружаться в устройство тех технологий и подходов, которые он применяет в дизайне.
Продвинутое проектирование. Кандидат самостоятельно формулирует архитектуру раздачи данных в реальном времени, четко объясняет устройство Pub/Sub, шардирование тем, балансировку соединений на уровне L7 и механизмы восстановления после обрывов связи.
Обоснование компромиссов. Каждое техническое решение сопровождается четким сравнением альтернатив (например, SSE против WebSockets, DynamoDB против реляционных баз данных, Redis Pub/Sub против Kafka). Кандидат объясняет выбор с точки зрения производительности, надежности и сложности эксплуатации.
Задача о Live-комментариях. Кандидат уровня Senior должен быстро пройти этап базовой архитектуры и оставить достаточно времени на подробное обсуждение масштабирования. Он должен разобраться в ограничениях начального решения и почти без подсказок предложить модель публикации и подписки. Затем ему нужно самостоятельно вести обсуждение масштабирования и объяснить компромиссы разных вариантов.
Staff+
Упор на предельную глубину. От кандидата уровня Staff+ ожидается глубокий разбор нетривиальных проблем и граничных случаев (примерно 40% широты и 60% глубины). Даже в незнакомом сценарии его фундаментальный опыт позволяет быстро выявлять узкие места и предлагать нестандартные инженерные решения.
Проактивность и видение системы. Кандидат ведет интервью самостоятельно, предвосхищая вопросы интервьюера. Он сразу выявляет ключевые сложности (например, поведение системы при трансляциях-миллионниках с тысячами комментариев в секунду) и предлагает элегантные архитектурные паттерны.
Переосмысление требований под экстремальные нагрузки. Кандидат понимает, когда классические подходы перестают работать из-за физических ограничений (пропускная способность сетей, человеческое восприятие). Он предлагает переход на семплирование и раздачу снимков через CDN, детально объясняет механики запаздывания, чтения собственных записей и дедупликации данных.
Задача о Live-комментариях. Кандидат уровня Staff+ демонстрирует системное мышление: детально разбирает различия в масштабировании обычных и сверхпопулярных трансляций, предлагает гибридную модель SSE + CDN, обосновывает граничные сценарии и стоимость поддержки инфраструктуры.