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

Рассылки

Рассылка (broadcast) — это сообщение, инициированное сервером, с именем действия и необязательными данными. Клиенты получают его в виде { "id": "@", "action": "…", "data": … }.

Публикуйте рассылку из обработчика через PublishAsync или из любого сервиса, внедрив IBroadcaster:

public sealed class OrderNotifier(IBroadcaster broadcaster) {
public Task OrderShippedAsync(Order order, CancellationToken ct) =>
broadcaster.PublishAsync(
BroadcastTarget.Group($"account:{order.AccountId}"),
"order:shipped",
new { order.Id, order.TrackingNumber },
ct);
}

PublishAsync(target, action) не отправляет поле data. Перегрузки с данными всегда записывают его — для значения null как JSON null, независимо от JsonOptions.DefaultIgnoreCondition.

Получатели​

BroadcastTarget определяет, кому будет доставлена рассылка:

ЦельПолучатели
BroadcastTarget.AllВсе подключения
BroadcastTarget.Connection(id)Одно подключение
BroadcastTarget.Session(id)Все подключения одной сессии, например все вкладки пользователя
BroadcastTarget.Group(name)Подключения, в сессии которых указана эта группа
BroadcastTarget.Groups(names)Объединение нескольких групп, по одному разу на подключение
Self (в обработчиках)Вызывающее подключение

Для групповых целей можно исключить получателей:

await PublishAsync(
BroadcastTarget.Groups(["account:42", "editors"]).ExceptConnection(Connection.Id),
"document:changed", new { DocumentId = 7 });

await PublishAsync(
BroadcastTarget.Group("account:42").ExceptSession(Session.Id),
"document:changed");
  • ExceptConnection пропускает одно подключение, обычно вызывающее.
  • ExceptSession пропускает все подключения сессии.
  • Если заданы оба исключения, пропускается подключение, подходящее под любое из них.
  • All, Connection и Session не поддерживают исключения.

Groups читает переданную последовательность один раз и удаляет дубликаты. Снимок групп неизменяем, в том числе через приведение к коллекции, и сохраняется в копиях Except. Изменение исходного списка не меняет цель рассылки. Пустая последовательность ничего не публикует, а у неизвестных групп нет получателей. Коллекции null и пустые идентификаторы, имена групп или имена действий отклоняются с исключением аргумента. Уже отменённый токен приводит к исключению до публикации.

Семантика доставки​

  • Выбор получателей использует один снимок индексов групп и сессий.
  • Сериализация выполняется один раз на рассылку; все получатели используют одни и те же байты.
  • Локальная доставка пишет всем получателям параллельно. Сокет, который не принимает запись в течение BroadcastSendTimeout (10 секунд), обрывается, не задерживая остальных.
  • Порядок на одном подключении не гарантируется. Если порядок важен, добавляйте в данные версию или порядковый номер.
  • Отмена токена публикующей стороны предотвращает публикацию, но не останавливает уже начавшуюся доставку.
  • Завершение: с in-memory backplane PublishAsync завершается после локальной доставки; с Redis — когда Redis принимает сообщение.
  • Без повторной доставки: отключённый клиент пропускает рассылку. Считайте рассылки уведомлениями об изменениях и заставляйте клиентов перезагружать состояние после переподключения.

Подключения и группы​

Внедрите IDarkWsConnections, чтобы просматривать локальные подключения:

public sealed class PresenceService(IDarkWsConnections connections) {
public int OnlineInAccount(Guid accountId) =>
connections.GetByGroup($"account:{accountId}").Count;
}
ЧленНазначение
Find(id)Одно подключение или null
GetAll()Все подключения на этом экземпляре
GetBySession(id)Подключения одной сессии
GetByGroup(name)Участники одной группы
Refresh(connection)Заново считывает группы сессии подключения в индексы

Снимок групп делается при добавлении подключения и после каждой повторной аутентификации. Если ваша сессия предоставляет группы, которые меняются независимо, вызовите затем Refresh(connection). Для закрытого или незарегистрированного подключения метод возвращает false, поэтому обновление, конкурирующее с отключением, не сможет зарегистрировать подключение заново.

Эти запросы видят только текущий экземпляр. С Redis рассылки всё равно доходят до подключений на всех экземплярах.

Backplane​

Broadcaster передаёт каждое сообщение в IDarkWsBackplane, который доставляет его на каждый экземпляр сервера. Стандартный in-memory backplane обслуживает один экземпляр. Для нескольких экземпляров используйте Redis backplane. Код обработчиков не меняется при смене backplane.

Собственный backplane реализует PublishAsync(BroadcastMessage), SubscribeAsync(listener) и UnsubscribeAsync(). Зарегистрируйте его как singleton IDarkWsBackplane через services.Replace(...) или через AddSingleton до вызова AddDarkWs().

Если вы создаёте BroadcastMessage напрямую, не изменяйте переданный список групп, пока сообщение используется: этот низкоуровневый DTO не копирует список.