Программа курса

Модуль 03 / Урок 11 из 27

Pub/Sub: модель и фундаментальные ограничения

Теория и практика

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-нагрузка уходит, и каналы «привязаны» к данным по хеш-тегам.

Практика

  1. Откройте два терминала, воспроизведите SUBSCRIBE/PUBLISH. Убедитесь, что публикация до подписки не доходит (PUBLISH вернул 0).
  2. Соберите инвалидатор локального кеша: под A пишет PUBLISH после DELETE ключа, под B держит словарь микро-кеша и чистит по сообщению. Проверьте, что после потери одного сообщения (остановите подписчика на секунду) система всё равно корректна благодаря TTL.
  3. Замерьте нагрузку: PUBLISH в канал с 0, 10 и 100 подписчиками (10 терминалов redis-cli SUBSCRIBE ch) — сравните QPS публикации.
Мини-квиз

1. Можно ли получить «сообщения за последний час» из Pub/Sub? — Нет, только из Streams.

2. Почему инвалидация кешей — легитимный сценарий Pub/Sub, хотя сообщения теряются? — Потеря компенсируется TTL ключа; сообщение лишь ускоряет очистку.

3. Чем SSUBSCRIBE лучше в кластере? — Маршрутизация только к владельцу слота вместо рассылки всем узлам.