Спроектируйте мессенджер
Вопрос
Спроектируйте мессенджер с личными и групповыми чатами, доставкой в реальном времени и историей.
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
Требования: личные и групповые чаты до 1 000 участников, доставка онлайн и офлайн (push), история, статусы «доставлено» и «прочитано»; порядок сообщений в чате; медиа и сквозное шифрование вне рамок
Оценка нагрузки: 50 млн DAU по 40 сообщений ≈ 2 млрд в сутки ≈ 20 000 RPS записи, в пике ~60 000; 10 млн одновременных соединений, ~100 000 на gateway → ~100 gateway; 2 млрд × 200 байт ≈ 400 ГБ в сутки, ~150 ТБ в год без реплик
API: WebSocket: send {chat_id, client_msg_id, text}, ack {msg_id}, события new_message, read; HTTP: GET /chats/{id}/messages?before=...
Данные: чаты и участники в PostgreSQL, сообщения в Cassandra/ScyllaDB с ключом партиции chat_id и сортировкой по msg_id, реестр user_id → gateway в Redis с TTL
Компоненты: gateway-серверы с соединениями, сервис сообщений (сохранить, присвоить id, разослать), сервис присутствия, push-сервис для офлайн, очередь для фан-аута в группах
Узкие места: большие группы (фан-аут через очередь), дубли при переотправке (client_msg_id для идемпотентности), переподключения и догрузка пропущенного, горячие чаты, падение gateway
Что могут спросить следом
- Как гарантировать порядок сообщений в чате?
- Что происходит, если получатель офлайн?
- Как не потерять сообщение при обрыве связи у отправителя?
Разбор
Требования: личные и групповые чаты, доставка в реальном времени, офлайн через push, история, статусы доставки, порядок внутри чата. Нагрузка: 50 млн DAU по 40 сообщений это 2 млрд в сутки, около 20 000 сообщений в секунду, в пике втрое больше. Онлайн одновременно около 10 млн, при 100 000 соединений на gateway нужно около 100 серверов. Объём около 400 ГБ в сутки. Клиент держит WebSocket с gateway. Отправка: клиент шлёт сообщение с client_msg_id, сервис сообщений сохраняет его в Cassandra с партицией chat_id, присваивает id, монотонный внутри чата, и отвечает ack, повтор с тем же client_msg_id не создаёт дубль. Затем по участникам чата смотрит в Redis, на каких gateway они сидят, и отправляет туда; офлайн-участникам уходит push. Клиент после переподключения догружает историю по последнему известному id. Большие группы раскладываю через очередь. Узкие места: падение gateway (клиенты переподключаются к другому, реестр обновляется по heartbeat), горячие чаты, рост хранилища, который решается шардированием по chat_id и холодным хранением старой истории.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.