Real-time: чат, уведомления
↑ 8.4 Frontend System Design · ← Предыдущая · → Следующая
Зачем это на собесе
Типовой кейс: дизайн чата или уведомлений. Важно выбрать транспорт и продумать надёжность.
Транспорты
| Способ | Особенности | Когда |
|---|---|---|
| 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.
Вопросы с ответами
SSE или WebSocket?
SSE — односторонние обновления с сервера, проще, работает через HTTP, авто-переподключение. WebSocket — двусторонний обмен с малой задержкой (чат, совместное редактирование).
Как гарантировать, что сообщения не потеряются при разрыве?
Серверные порядковые номера, клиент запоминает последний
seqи после переподключения запрашивает пропущенное; отправка сclientMsgIdи подтверждения.