Real-Time Backend
Socket.IO advanced
Цели урока
- Реализовать JWT-аутентификацию через Socket.IO middleware
- Передавать бинарные данные без base64 overhead
- Отличать volatile от обычных событий и применять в правильных сценариях
- Настроить Redis Adapter для масштабирования на несколько инстансов
Figma в 2019 году обслуживала миллионы сессий collaborative редактирования. Проблема: позиции курсоров всех участников нужно транслировать в реальном времени, но при 20 пользователях в документе это 20*30 = 600 событий в секунду. Решение - volatile events для курсоров и обычные события для изменений документа. Redis Adapter позволил масштабироваться горизонтально.
- **Figma** - volatile events для курсоров участников, обычные для операций документа
- **Socket.IO в Kubernetes** - Redis Adapter стандартен при деплое в несколько pod-ов
- **Real-time игры** - binary transfer для игрового стейта, volatile для позиций игроков
Socket.IO Middleware и аутентификация
HTTP middleware останавливает неаутентифицированный запрос на входе. В Socket.IO та же логика работает через **middleware** - функцию, которая вызывается при каждом новом подключении до того, как сокет получит первое событие. Это единственное место, где можно проверить JWT, сессию или API-ключ и отклонить соединение.
Middleware можно цеплять в цепочку: первое проверяет JWT, второе проверяет rate limit, третье логирует. Каждое получает тот же `socket` объект и `next`. **Важно**: middleware работает только при установке соединения - для авторизации отдельных событий нужны дополнительные проверки внутри обработчиков.
Namespace middleware выполняется только для сокетов этого namespace. Это позволяет иметь разную авторизацию для `/public` и `/admin` без дублирования логики.
Middleware в Socket.IO вызывается:
Binary данные в Socket.IO
Передача изображения через Socket.IO наивным способом - это base64: изображение 1 МБ становится 1.37 МБ текста. Socket.IO поддерживает нативную передачу **бинарных данных** через `ArrayBuffer` и `Buffer` - без кодирования, с минимальным оверхедом. Socket.IO автоматически детектирует бинарные данные в payload и использует специальный бинарный пакетный тип.
Socket.IO использует специальный формат для бинарных пакетов: объект с `_placeholder: true` для каждого бинарного буфера, затем отдельные бинарные кадры. Это позволяет смешивать JSON-метаданные и бинарные данные в одном событии.
Почему передавать файлы через Socket.IO как Buffer лучше, чем как base64-строку?
Volatile events и буферизация
Координаты курсора в collaborative редакторе отправляются 30 раз в секунду. Если клиент временно недоступен (вкладка в фоне, плохая связь) - буферизовать 300 устаревших позиций бессмысленно: актуальна только последняя. **Volatile events** решают это: если клиент не готов принять событие в данный момент, оно просто отбрасывается.
Какое из событий имеет смысл отправлять как volatile?
Socket.IO Adapters: Redis Adapter
Один Socket.IO сервер держит до ~10 000 соединений. При горизонтальном масштабировании (3 инстанса) возникает проблема: сокет A подключён к инстансу 1, сокет B - к инстансу 2. Вызов `io.to('room-42').emit(...)` на инстансе 1 дойдёт только до сокетов этого инстанса. **Adapter** решает проблему, делая broadcast межпроцессным.
Redis Adapter добавляет задержку ~1-2 мс на каждый broadcast (round-trip через Redis). Для большинства real-time приложений это незаметно. Если это критично - рассмотрите Cluster Adapter для одного хоста или sticky sessions + namespace sharding.
Без Redis Adapter при горизонтальном масштабировании Socket.IO на 3 инстанса `io.to('room').emit()` отправит событие:
Socket.IO advanced
- Middleware аутентифицирует при connect, результат сохраняется в socket объекте
- Buffer/ArrayBuffer передаётся нативно без base64, с нулевым overhead
- Volatile events отбрасываются если клиент не готов - для high-frequency данных
- Redis Adapter синхронизирует broadcast между инстансами через Pub/Sub
- Масштабирование: каждый инстанс видит только свои сокеты без adapter
Связанные темы
Advanced функции Socket.IO решают задачи безопасности и масштабирования реального продакшна.
- Socket.IO основы — Handshake, Rooms, Namespaces и Acknowledgements - фундамент advanced возможностей
- Redis Pub/Sub — Redis Adapter использует Pub/Sub как транспорт для межпроцессного broadcast
- Масштабирование WebSocket серверов — Горизонтальное масштабирование Socket.IO через адаптеры и sticky sessions
Вопросы для размышления
- Как реализовать авторизацию на уровне отдельных событий, а не только при подключении?
- В каких сценариях Redis Streams Adapter предпочтительнее Redis Pub/Sub Adapter?
- Как volatile events влияют на UX в collaborative приложениях при плохом соединении?
Связанные уроки
- rt-09 — Advanced features assume mastery of basic Socket.IO API from rt-09
- rt-11 — uWebSockets.js vs Socket.IO advanced represents the performance vs developer-experience tradeoff
- rt-08 — Binary protocol support in Socket.IO advanced connects back to the serialization lesson
- rt-05 — Knowing WebSocket deeply helps when Socket.IO advanced features (like transport switching) behave unexpectedly
- net-36-websocket