Рассылки
Рассылка (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 не копирует список.