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
приложения:
| Поле | Значение |
|---|---|
target | 0 All, 1 Connection, 2 Session, 3 Group, 4 Groups |
targetId | Идентификатор подключения, сессии или группы; null для All и Groups |
action | Действие рассылки |
data | Payload приложения, сериализованный с JsonOptions приложения |
groups | Имена групп (только для Groups) |
except | Необязательные исключения connectionId и sessionId (только для Groups) |
Числовые значения целей никогда не меняются.
Объединения групп и любая цель с исключением используют target: 4, даже для
одной группы. Экземпляры версий ниже 5.0 отклоняют эту цель и пропускают доставку, а не
игнорируют исключение. Обновите все серверы, использующие общий канал, прежде чем
применять Groups, ExceptConnection или ExceptSession, либо выкатывайте изменения на
новом канале. Сообщения без исключений сохраняют прежний формат с одной целью.
Одна подписка
Backplane поддерживает одну активную подписку; DarkWS управляет ею за вас. Собственный
хост, подписывающийся напрямую, должен отписаться перед повторной подпиской, поскольку
повторный SubscribeAsync выбрасывает InvalidOperationException.