Спроектируйте мессенджер уровня WhatsApp: личные и групповые чаты, статусы доставки и прочтения, работа при нестабильной сети.
Короткий ответ
- Постоянное соединение: WebSocket, для мобильных — плюс push
- Сервис-шлюз держит соединения, реестр знает, кто где подключён
- Сообщения персистентны до подтверждения доставки
- Порядок в чате задаёт серверный монотонный номер
- Групповой чат — fan-out по участникам через очередь
- Идемпотентность по client message id против дублей
- Офлайн-пользователю сообщение ждёт в inbox и уходит пушем
Мессенджер строится на WebSocket-шлюзах с реестром присутствия, персистентной очередью сообщений, серверной нумерацией для порядка и идемпотентностью для надёжной доставки.
Как сказать вслух
пример ответаЯ разделю систему на шлюзы, которые держат WebSocket-соединения, и сервис сообщений, который пишет всё в хранилище до подтверждения. Порядок внутри чата обеспечу серверным номером сообщения. Дубли при ретраях отсеку по идентификатору, который генерирует клиент.
Подробный ответ
Основной ответ
Клиент держит WebSocket с одним из шлюзов; реестр присутствия (например, в Redis) сопоставляет пользователя и шлюз. Отправка: клиент шлёт сообщение со своим client id, сервис сообщений сохраняет его в хранилище (Cassandra/HBase, партиционирование по chat id, кластеризация по номеру сообщения), присваивает серверный монотонный номер и отдаёт ack. Затем доставка: по реестру находим шлюз получателя и пушим; офлайн-получателю сообщение остаётся в его inbox и дублируется мобильным пушем. Статусы sent/delivered/read — отдельные служебные события. Группы — fan-out по списку участников через очередь. Ретраи клиента безопасны: сервер дедуплицирует по client id.
Ключевые моменты
- Порядок сообщений. Полагаться на часы клиентов нельзя; монотонный номер в рамках чата выдаёт сервер, и клиенты сортируют по нему.
- Надёжная доставка. Сообщение считается принятым только после записи в хранилище; клиент ретраит до ack, сервер дедуплицирует — получаем at-least-once без дублей для пользователя.
- Реестр присутствия. Пользователи размазаны по шлюзам, поэтому нужен быстрый справочник «кто на каком шлюзе» с heartbeat и очисткой мёртвых сессий.
- Большие группы. Fan-out на тысячи участников делается асинхронно воркерами; для очень больших групп доставку заменяют подпиской на канал.
Практический контекст
Интервьюер ждёт обсуждения гарантий: порядок, exactly-once для пользователя, поведение при обрыве сети и переподключении (синхронизация по последнему полученному номеру). Уточните: нужен ли E2E-шифрование, история на новых устройствах, максимальный размер группы, индикатор «печатает». Сильный ход — описать переподключение клиента и догрузку пропущенного как обычный pull по номеру.
Частые ошибки
- Доставляют сообщения напрямую между шлюзами без персистентности — при падении узла сообщения теряются
- Упорядочивают чат по таймстемпам клиентов, которые расходятся между устройствами
- Забывают сценарий переподключения: клиент после обрыва не знает, что пропустил