Real-time: чат, уведомления

↑ 8.4 Frontend System Design · ← Предыдущая · → Следующая

Желательно~3 мин чтенияУровень: senior

Зачем это на собесе

Типовой кейс: дизайн чата или уведомлений. Важно выбрать транспорт и продумать надёжность.

Транспорты

СпособОсобенностиКогда
Pollingпериодические запросыпростое, редкие обновления
Long pollingзапрос висит до событияfallback
SSE (Server-Sent Events)односторонний поток от сервера, авто-переподключениеуведомления, лента, прогресс
WebSocketдвусторонний каналчат, коллаборация, игры
WebTransportна QUICпродвинутые сценарии
Push (Web Push)вне вкладкиважные уведомления

Дизайн чата

Client ──WebSocket──▶ Gateway ──▶ Chat service ──▶ DB + Pub/Sub (Redis/Kafka)

Клиент:

  • состояние соединения: connecting, open, reconnecting, closed;
  • переподключение с экспоненциальной задержкой и jitter;
  • heartbeat (ping/pong) для обнаружения обрыва;
  • упорядочивание: серверный seq или timestamp;
  • доставка: clientMsgId для идемпотентности и дедупликации, статусы «отправляется / доставлено / прочитано»;
  • оффлайн-очередь отправки, повтор после восстановления;
  • синхронизация после разрыва: запрос пропущенных сообщений since=lastSeq;
  • оптимистичное добавление, откат при ошибке;
  • индикатор «печатает» с throttle;
  • виртуализация списка, подгрузка истории вверх.
class Socket {
  #ws?: WebSocket; #attempt = 0
  connect() {
    this.#ws = new WebSocket(url)
    this.#ws.onopen = () => { this.#attempt = 0; this.resync() }
    this.#ws.onclose = () => setTimeout(() => this.connect(), Math.min(30_000, 2 ** this.#attempt++ * 1000) + Math.random() * 500)
    this.#ws.onmessage = e => this.handle(JSON.parse(e.data))
  }
}

Уведомления

  • SSE или WebSocket для активной вкладки, Web Push для закрытой;
  • счётчик непрочитанных и список; синхронизация между вкладками (BroadcastChannel);
  • группировка и ограничение частоты;
  • разрешение на push — по действию пользователя.

Масштаб и надёжность

  • бэкенд: горизонтальное масштабирование, pub/sub между узлами;
  • аутентификация соединения (токен при подключении, обновление);
  • лимиты и backpressure;
  • деградация: при недоступности WebSocket — polling.

Вопросы с ответами