Pub/Sub — механизм «радиовещания»: сообщение доставляется всем подписчикам канала в момент публикации и нигде не сохраняется. Из этой модели следуют и все его достоинства, и все ограничения.
Как это работает
# Терминал 1 — подписчик
SUBSCRIBE orders.created orders.paid
1) "subscribe" 2) "orders.created" 3) (integer) 1
# Терминал 2 — издатель
PUBLISH orders.created '{"id":42,"total":990}'
(integer) 1 # сколько подписчиков получили сообщение СЕЙЧАС
Каналы не нужно создавать: их не существует, пока нет подписчиков. Есть шаблонная подписка:
PSUBSCRIBE orders.* # все события домена orders
Ответ PUBLISH — число реально получивших подписчиков. 0 означает, что сообщение ушло в пустоту навсегда.
Ограничения — почему это НЕ очередь
- Нет хранения: сообщения живут только в момент форвардинга. Подключились позже — прошлого нет;
- Нет ACK: если подписчик принял сообщение и упал до обработки — сообщение потеряно для него;
- Нет ретраев и DLQ;
- Нет дедупликации и порядка-гарантий на уровне подписчика (порядок отправки на канал сохраняется, но ваш обработчик может быть медленным);
- Ретрансляция каждому: 100 подписчиков = 100 копий в сеть и память сервера. Публикация на канал с тысячами подписчиков — заметная нагрузка на однопоточный Redis.
⚠️ Правило: Pub/Sub — только для потерю которых переживёте событий: инвалидация кешей, «live»-уведомления в UI, сигналы «перезагрузи конфиг», heartbeat'ы. Всё, что нельзя потерять, — Streams (следующие уроки).
Где Pub/Sub действительно хорош
- Инвалидация кешей между подами: под A обновил данные и сделал
PUBLISH inv user:1001— остальные поды выкидывают ключ из локального микро-кеша. Потеря сообщения не страшна: спасёт TTL ключа; - Real-time в браузер/мобильный клиент через шлюз (WebSocket-прокси подписан на каналы);
- Координация конфигов/feature-flags: «изменился флаг — перечитайте»;
- Транзит метрик/событий отладки, где потеря — просто недостающая точка на графике.
Шардированный Pub/Sub (Redis 7+)
Классический Pub/Sub в кластере рассылает сообщения по всем узлам — на большом кластере это накладные расходы. Шардированный вариант маршрутизирует по слоту канала:
SSUBSCRIBE orders.created # подписка на шард-канал
SPUBLISH orders.created '...' # публикация — только к владельцу слота
Модель та же (fire-and-forget), но broadcast-нагрузка уходит, и каналы «привязаны» к данным по хеш-тегам.
Практика
- Откройте два терминала, воспроизведите SUBSCRIBE/PUBLISH. Убедитесь, что публикация до подписки не доходит (PUBLISH вернул 0).
- Соберите инвалидатор локального кеша: под A пишет PUBLISH после DELETE ключа, под B держит словарь микро-кеша и чистит по сообщению. Проверьте, что после потери одного сообщения (остановите подписчика на секунду) система всё равно корректна благодаря TTL.
- Замерьте нагрузку: PUBLISH в канал с 0, 10 и 100 подписчиками (10 терминалов
redis-cli SUBSCRIBE ch) — сравните QPS публикации.
Мини-квиз
1. Можно ли получить «сообщения за последний час» из Pub/Sub? — Нет, только из Streams.
2. Почему инвалидация кешей — легитимный сценарий Pub/Sub, хотя сообщения теряются? — Потеря компенсируется TTL ключа; сообщение лишь ускоряет очистку.
3. Чем SSUBSCRIBE лучше в кластере? — Маршрутизация только к владельцу слота вместо рассылки всем узлам.