Мессенджер и онлайн-чат

Реалтайм-задача: спроектировать чат с мгновенной доставкой, статусами и историей.

WebSocket — постоянное двунаправленное соединение между клиентом и сервером, позволяющее серверу пушить сообщения клиенту без опроса (polling).

Требования

Функционально: отправить/получить сообщение в реальном времени, видеть статусы (доставлено/прочитано), presence (онлайн/офлайн), история переписки. Нефункционально: низкая латентность доставки, надёжность (сообщение не теряется), порядок сообщений в диалоге.

Транспорт: WebSocket

HTTP-опрос для чата неэффективен. WebSocket держит постоянное соединение: когда приходит сообщение для пользователя, сервер сразу пушит его в открытый сокет. Но соединения stateful — пользователь «висит» на конкретном узле.

Юзер A -- WS --> Gateway #2 -> [Очередь/маршрутизация] -> Gateway #5 -- WS --> Юзер B

Маршрутизация и хранение

ЗадачаРешение
Найти, на каком узле юзерРеестр сессий (Redis: user -> gateway)
Получатель офлайнСохранить как недоставленное, доставить при входе
История сообщенийКолоночная БД (по диалогу + времени)
Порядок сообщенийSequence id / timestamp на диалог

Как работает под капотом

Поскольку WebSocket stateful, балансировка идёт по соединениям, а маршрутизация сообщения между пользователями требует знать, к какому gateway подключён получатель — это держат в общем реестре сессий. Доставку делают надёжной через подтверждения (ack) и хранение: сообщение сперва персистится, потом пушится; если получатель офлайн — лежит до подключения. Статусы (доставлено/прочитано) — это отдельные служебные события в обратную сторону. История диалогов хорошо ложится на колоночную БД с ключом «диалог + время».

Частые ошибки

  • Опрос HTTP вместо WebSocket для реалтайма.
  • Пушить сообщение без предварительного сохранения -> потеря при сбое.
  • Игнорировать порядок сообщений и реестр сессий для маршрутизации.

Итог

  • Реалтайм-доставка — через WebSocket; соединения stateful.
  • Маршрутизация требует реестра сессий (user -> gateway).
  • Сначала персист, потом push; офлайн-сообщения копятся до входа.
Проверьте себя
1. Почему для чата предпочитают WebSocket, а не HTTP-опрос?
AWebSocket проще реализовать
BПостоянное соединение позволяет серверу мгновенно пушить сообщения без опроса
CHTTP не умеет передавать текст
DWebSocket не требует серверов
2. Зачем нужен реестр сессий (user -> gateway) в Redis?
AДля хранения истории
BЧтобы знать, к какому узлу подключён получатель, и доставить ему сообщение
CДля шифрования сообщений
DДля подсчёта лайков
3. Как правильно обеспечить надёжность доставки сообщения?
AСразу пушить, не сохраняя
BСначала сохранить (персист), потом пушить; офлайн-сообщения хранить до входа
CУдалять сообщение после первой попытки
DНе использовать БД вообще