Real-Time Backend
Socket.IO основы
Цели урока
- Понять механизм handshake и upgrade в Socket.IO
- Отличать Namespace от Room и выбирать подходящий уровень изоляции
- Использовать Acknowledgements для надёжной доставки событий
- Понимать fallback-стратегию polling и её trade-offs
WhatsApp в 2016 году обслуживал 1 миллиард пользователей командой из 50 инженеров. Основа - протокол с переподключением, acknowledgements и мультиплексированием. Socket.IO предоставляет всё это из коробки за 5 строк кода.
- **Slack** - Namespace для workspace, Rooms для каналов, Ack для гарантии доставки
- **Figma** - real-time collaboration: каждый документ - отдельная Room в Socket.IO
- **Trello** - live updates с fallback на polling для корпоративных клиентов за прокси
Socket.IO: handshake и upgrade
Браузер открывает вкладку чата и хочет получать сообщения в реальном времени. HTTP - это запрос-ответ: сервер не может ничего отправить первым. **Socket.IO** решает проблему в три шага: сначала устанавливает соединение через HTTP long-polling (работает везде), затем пробует сделать **upgrade до WebSocket**, и если upgrade прошёл - переходит на него навсегда.
Первый запрос - это **handshake**: клиент получает `sid` (идентификатор сессии), `pingInterval`, `pingTimeout` и список доступных транспортов. Затем начинается polling, и параллельно клиент делает WebSocket upgrade запрос.
Socket.IO - это **не** просто WebSocket. Поверх него (или polling) добавляется свой протокол: автоматические ping/pong, переподключение при разрыве, acknowledgements и мультиплексирование namespaces.
Socket.IO при подключении начинает с long-polling, а не сразу с WebSocket. Главная причина:
Rooms и Namespaces
В мессенджере тысячи чатов, но пользователь должен получать сообщения только из своих. Socket.IO предлагает два уровня группировки: **Namespace** (разные приложения на одном сервере) и **Room** (группы сокетов внутри namespace).
**Namespace** - это отдельный endpoint: `/chat`, `/admin`, `/notifications`. Разные namespaces изолированы - события из `/chat` не попадают в `/admin`. **Room** - временная группа внутри namespace. Сокет может входить в несколько rooms одновременно, и каждый сокет автоматически входит в персональный room с именем `socket.id`.
Когда лучше использовать отдельный Namespace вместо Room?
Acknowledgements и надёжность
WebSocket - fire-and-forget: отправил пакет, и неизвестно, дошёл ли он. Для чата это катастрофа - пользователь видит галочку «отправлено», а сообщение потерялось. **Acknowledgements** (ack) решают это: отправитель ждёт явного подтверждения от получателя, прежде чем считать событие доставленным.
Ack - это не автоматическая гарантия доставки. Это механизм **подтверждения прикладного уровня**: сервер получил, обработал и вернул результат. Если соединение разрывается до прихода ack - Socket.IO переподключится, но повторной отправки события не будет. Это ответственность приложения.
Для at-least-once гарантии нужна идемпотентная логика: клиент сохраняет событие с уникальным `clientEventId` и повторяет при переподключении; сервер дедуплицирует по этому id.
Acknowledgement в Socket.IO гарантирует, что сообщение:
Fallback: polling транспорт
Корпоративная сеть часто блокирует WebSocket: прокси не понимает `Upgrade: websocket` и обрывает соединение. Socket.IO автоматически откатывается на **HTTP long-polling** - браузер делает GET-запрос, сервер держит его открытым до появления события, затем клиент сразу делает следующий запрос.
Если WebSocket недоступен из-за прокси и нужна минимальная задержка - рассмотрите **Server-Sent Events** для сервер→клиент и HTTP POST для клиент→сервер. SSE проходит через большинство прокси и быстрее polling.
Socket.IO с `transports: ['polling', 'websocket']`. WebSocket заблокирован корпоративным прокси. Результат:
Socket.IO основы
- Handshake выдаёт sid, начинает с polling, затем upgrade до WebSocket
- Namespace - изоляция логики и middleware (разные endpoints приложения)
- Room - динамические группы внутри namespace для targeted broadcast
- Acknowledgements: callback подтверждает обработку на прикладном уровне
- Fallback на polling прозрачен для приложения, API не меняется
Связанные темы
Socket.IO строится поверх WebSocket и решает задачи, которые встречаются во всём real-time стеке.
- WebSocket протокол — Socket.IO использует WebSocket как основной транспорт, добавляя свой протокол поверх
- Server-Sent Events — Альтернатива polling транспорту для направления сервер→клиент
- Socket.IO Advanced — Middleware, binary data, volatile events и Redis Adapter для масштабирования
Вопросы для размышления
- Когда имеет смысл отказаться от Socket.IO в пользу нативного WebSocket API?
- Как реализовать at-least-once доставку поверх acknowledgements при нестабильном соединении?
- В каких сценариях Namespace предпочтительнее отдельных Socket.IO серверов?
Связанные уроки
- rt-05 — Socket.IO runs on WebSocket by default; understanding the transport is required
- rt-08 — Socket.IO serialization choices (JSON + binary) become clear after the serialization lesson
- rt-10 — Socket.IO advanced features (rooms, namespaces, adapters) build directly on the basics
- rt-06 — Socket.IO is one of the 'approaches' compared in rt-06; seeing the comparison motivates the library choice
- net-36-websocket