Масштабирование: несколько серверов и Redis pub/sub
Что ломается, когда серверов становится несколько, и как это чинят.
Pub/sub (издатель-подписчик) — модель обмена, где сообщение публикуют в «канал», а все подписчики на этот канал его получают; используется, чтобы связать несколько серверов.
Проблема нескольких серверов
Один сервер не выдержит миллион соединений — приложение масштабируют горизонтально: ставят несколько серверов за балансировщиком. Но возникает беда: соединения «размазаны». Анна подключена к серверу №1, Иван — к серверу №2. Анна шлёт сообщение в комнату, сервер №1 разошлёт его только своим клиентам — Иван на сервере №2 ничего не получит. Список клиентов у каждого сервера свой.
Анна Иван
| |
Сервер 1 Сервер 2
(знает A) (знает И)
\ /
broadcast от Анны НЕ дойдёт до ИванаРешение: Redis pub/sub
Серверы связывают общей шиной — обычно Redis. Когда сервер получает сообщение, он не только шлёт своим клиентам, но и публикует его в Redis-канал. Все остальные серверы подписаны на этот канал, получают сообщение и рассылают уже своим клиентам.
Анна --> Сервер 1 --publish--> [ Redis ] --notify--> Сервер 2 --> Иван
|
своим клиентам Sticky sessions
Ещё одна тонкость — балансировщик. WebSocket держит постоянное соединение, поэтому все пакеты одного клиента должны идти на тот же сервер, что принял рукопожатие. Это обеспечивают sticky sessions (закрепление клиента за сервером). Без них балансировщик может перекинуть клиента на другой сервер посреди соединения — и канал порвётся.
Как работает под капотом
Redis pub/sub — это не хранилище сообщений, а быстрый «громкоговоритель»: опубликованное доходит только тем, кто подписан сейчас. Поэтому он отвечает за рассылку между серверами, а историю чата (чтобы догрузить сообщения после реконнекта) хранят отдельно — в базе. У Socket.IO для этого есть готовый «адаптер» Redis, который берёт публикацию и подписку на себя. По сути это превращает много серверов в один логический broadcast-домен.
Частые ошибки
- Масштабировать без общей шины. Клиенты на разных серверах не увидят друг друга — нужен Redis pub/sub или аналог.
- Забыть sticky sessions. Балансировщик порвёт соединение, перекинув клиента на другой сервер.
- Полагаться на Redis pub/sub как на хранилище. Он не хранит сообщения; историю держите в базе.
Итоги
- При нескольких серверах их клиенты изолированы — broadcast не пересекает границу сервера.
- Серверы связывают шиной pub/sub (обычно Redis): один публикует, остальные рассылают своим.
- Sticky sessions держат клиента на одном сервере всё соединение.
- Redis pub/sub не хранит историю — её ведут в базе отдельно.