Перейти к основному содержимому
Версия: 5.x

Redis backplane

Когда за балансировщиком нагрузки работает несколько экземпляров сервера, рассылка, опубликованная на одном экземпляре, должна дойти до клиентов, подключённых к остальным. DarkWS.Redis распространяет рассылки через Redis Pub/Sub.

dotnet add package DarkWS.Redis

Зарегистрируйте IConnectionMultiplexer и добавьте Redis с явным именем канала:

using DarkWS.Redis;
using StackExchange.Redis;

var redis = await ConnectionMultiplexer.ConnectAsync(builder.Configuration["Redis"]!);
builder.Services.AddSingleton<IConnectionMultiplexer>(redis);

builder.Services
.AddDarkWs()
.AddHandlersFromAssemblyContaining<Program>()
.AddRedis("my-app:production", options => {
options.QueueCapacity = 256;
options.MaxMessageSizeBytes = 1024 * 1024;
});
  • Используйте уникальный канал для каждого приложения и окружения.
  • Вызывайте AddRedis один раз; повторный вызов выбрасывает InvalidOperationException, а не переключает канал молча.
  • Мультиплексором и его временем жизни владеет хост.
  • Код обработчиков не меняется.
  • Callback настройки необязателен; показаны значения по умолчанию. Лимиты должны быть положительными и копируются при регистрации.

Пакет требует StackExchange.Redis 2.13.17 или новее и протестирован с версиями 2.13.17 и 3.2.1.

Гарантии доставки​

Redis Pub/Sub доставляет сообщения не более одного раза. Рассылки, опубликованные, пока экземпляр отключён от Redis (перезапуск, failover, потеря сети), никогда не дойдут до клиентов этого экземпляра, и о пропуске ничто не сообщит.

Считайте рассылки уведомлениями об изменениях, а не источником состояния. После IConnectionMultiplexer.ConnectionRestored, как и после переподключения клиента, заставляйте клиентов перезагружать данные из источника истины.

Пропускная способность и порядок​

  • Каждый экземпляр выполняет до 16 доставок параллельно, поэтому медленный получатель не задерживает несвязанные рассылки.
  • Записи в один сокет остаются последовательными, но порядок рассылок на подключении не гарантируется. Если порядок важен, добавляйте версию или порядковый номер.
  • PublishAsync завершается, когда Redis принимает сообщение, а не после доставки.
  • Callback Redis передаёт сообщения в ограниченную локальную очередь, не ожидая доставки. Она вмещает не более QueueCapacity сообщений; при заполнении отбрасывается новое сообщение. Повторной доставки и отключения при переполнении нет, другие публикующие стороны продолжают работать независимо.
  • Если сериализованный конверт превышает MaxMessageSizeBytes, PublishAsync выбрасывает ArgumentException до публикации. Слишком большие сообщения от других публикующих сторон отбрасываются до десериализации.
  • Объём wire payload в очереди ограничен QueueCapacity * MaxMessageSizeBytes; ещё до 16 сообщений могут доставляться. Это не лимит памяти процесса: сериализация на стороне публикации, буферы зависимости/сети и прикладные аллокации находятся вне этой очереди. Частоту публикаций также ограничивайте в хосте.

Meter DarkWS.Redis предоставляет счётчики с тегом channel:

ИнструментЗначение
darkws.redis.receivedСообщения, полученные активной подпиской, включая отброшенные
darkws.redis.droppedОтброшенные входящие сообщения с reason=capacity или reason=oversize
darkws.redis.rejectedЛокальные публикации, отклонённые из-за превышения размера конверта

Используйте MeterListener или экспортёр метрик OpenTelemetry. Успешная публикация по-прежнему означает лишь приём сообщения Redis и не сообщает о переполнении у подписчика. Подбирайте лимиты и перечитывайте состояние приложения, если пропуски существенны.

Граница доверия​

Любой, кто может публиковать в канал, может отправлять произвольные уведомления клиентам на всех экземплярах, включая конкретные сессии и группы.

  • Изолируйте Redis на уровне сетевого доступа и ограничьте каналы с помощью ACL.
  • Уникальные имена каналов разделяют окружения, но ничего не авторизуют.
  • Номера логических баз данных не изолируют каналы Pub/Sub (документация Redis).
  • DarkWsOptions.MaxMessageSizeBytes ограничивает входящие данные WebSocket. Независимый DarkWsRedisOptions.MaxMessageSizeBytes ограничивает весь конверт Redis.

Формат сообщений​

Конверт Redis использует фиксированные настройки JSON, не зависящие от JsonOptions приложения:

ПолеЗначение
target0 All, 1 Connection, 2 Session, 3 Group, 4 Groups
targetIdИдентификатор подключения, сессии или группы; null для All и Groups
actionДействие рассылки
dataPayload приложения, сериализованный с JsonOptions приложения
groupsИмена групп (только для Groups)
exceptНеобязательные исключения connectionId и sessionId (только для Groups)

Числовые значения целей никогда не меняются.

Объединения групп и любая цель с исключением используют target: 4, даже для одной группы. Экземпляры версий ниже 5.0 отклоняют эту цель и пропускают доставку, а не игнорируют исключение. Обновите все серверы, использующие общий канал, прежде чем применять Groups, ExceptConnection или ExceptSession, либо выкатывайте изменения на новом канале. Сообщения без исключений сохраняют прежний формат с одной целью.

Одна подписка​

Backplane поддерживает одну активную подписку; DarkWS управляет ею за вас. Собственный хост, подписывающийся напрямую, должен отписаться перед повторной подпиской, поскольку повторный SubscribeAsync выбрасывает InvalidOperationException.