Конфигурация и ограничения
Настраивайте DarkWsOptions через AddDarkWs, services.Configure<DarkWsOptions>,
привязку конфигурации или PostConfigure; они применяются в стандартном порядке Options:
builder.Services.AddDarkWs(options => {
options.MaxMessageSizeBytes = 256 * 1024;
options.MaxConcurrentRequestsPerConnection = 8;
options.AllowedOrigins.Add("https://app.example.com");
});
builder.Services.Configure<DarkWsOptions>(builder.Configuration.GetSection("DarkWs"));
Опции валидируются при первом разрешении и при запуске хоста; недопустимые значения
приводят к OptionsValidationException. Подключения и singleton-сервисы запоминают
используемые опции, поэтому изменение опций во время работы не перенастраивает
существующие подключения.
Опции
| Опция | По умолчанию | Значение |
|---|---|---|
MaxMessageSizeBytes | 1 MiB | Максимальный размер полного входящего сообщения с учётом всех фрагментов |
JsonOptions | JsonSerializerDefaults.Web | Сериализация конвертов и payload |
AllowNullPayloads | false | Передавать null в non-nullable ссылочные параметры |
MaxConcurrentRequestsPerConnection | 16 | Число одновременно выполняемых действий на одном подключении |
RequestQueueTimeout | 5 s | Ожидание места в очереди до BusyError |
RunActionsOnThreadPool | false | Запускать действия в пуле потоков |
KeepAliveInterval | 30 s | Интервал keep-alive транспорта |
KeepAliveTimeout | 30 s | Дедлайн PONG на уровне транспорта (.NET 9 и новее) |
ReceiveIdleTimeout | 2 min | Дедлайн ожидающего чтения (.NET 8) |
SendTimeout | 30 s | Ожидание блокировки отправки плюс запись в сокет |
BroadcastSendTimeout | 10 s | Дедлайн записи рассылки для каждого получателя |
ShutdownTimeout | 10 s | Обработчики, хуки закрытия и close handshake при остановке |
AllowedOrigins | пусто (любые) | Браузерные origin, которым разрешено открывать эндпоинт |
AuthenticationQueryParameter | token | Параметр запроса, передаваемый аутентификатору при upgrade |
AcceptAnonymousOnUpgradeAuthenticationException | false | Принимать анонимно, если аутентификатор при upgrade выбрасывает исключение |
KeepSessionOnFailedAuthentication | false | Сохранять сессию после неудачного auth: |
InvalidActionError | darkws:error:invalid-action | Код для неизвестных действий |
InvalidRequestError | darkws:error:invalid-request | Код для некорректных запросов и payload |
AuthorizationRequiredError | darkws:error:authorization-required | Код для анонимных вызовов защищённых действий |
RequestFailedError | darkws:error:request-failed | Код для непредвиденных сбоев |
BusyError | darkws:error:busy | Код для запросов, отклонённых из-за заполненной очереди |
Все таймауты должны быть положительными и не превышать 4294967294 миллисекунды.
Размер сообщения
MaxMessageSizeBytes проверяется до разбора JSON и охватывает все фрагменты
сообщения, включая команды аутентификации. Сообщение, размер которого ровно равен
лимиту, принимается. Более крупное сообщение закрывает подключение со статусом 1009
(Message Too Big), и частично полученное сообщение не обрабатывается. Повышайте лимит,
только если приложение действительно отправляет большие сообщения: вместе с ним растёт
память на подключение.
Конкурентность запросов и backpressure
У каждого подключения своя очередь:
- Одновременно выполняется до
MaxConcurrentRequestsPerConnectionзапросов (от 1 доint.MaxValue - 4). - Ещё столько же обычных запросов ожидают в порядке FIFO.
- Четыре дополнительных места зарезервированы для
auth:иlogout, которые сохраняют свой порядок относительно запросов. - Отдельная входная очередь вмещает до
MaxConcurrentRequestsPerConnection + 4сообщений, пока один обработчик допуска ожидает места в очереди выполнения. Порядок поступления запросов и команд сохраняется. - Транспортные PONG продолжают читаться под нагрузкой. На текстовый
pingотвечает отдельный обработчик с одним ожидающим слотом; повторные ping объединяются, пока запись занята.
Обычный запрос, ожидающий места для выполнения дольше RequestQueueTimeout, получает
darkws:error:busy. Чтение сокета продолжается во время ожидания и отправки ответов.
Переполнение входной очереди или допуск команды в заполненную очередь выполнения
закрывает подключение со статусом 1008 (Policy Violation).
Клиентам следует ограничивать незавершённые вызовы и отправлять следующие порции по
мере получения ответов, чтобы не переполнять входную очередь. RequestQueueTimeout
не обязан быть меньше KeepAliveTimeout. Закладывайте память на выполняемые запросы,
обе ограниченные очереди, одно допускаемое и одно принимаемое сообщение, каждое
размером до MaxMessageSizeBytes, на каждое подключение.
DarkWS ограничивает работу только внутри одного подключения. Общее число подключений и лимиты на пользователя или IP задавайте в хосте или reverse proxy.
Проверка активности
DarkWS обнаруживает «мёртвые» пиры по-разному в зависимости от рантайма:
- .NET 9 и 10: сервер отправляет транспортные PING каждые
KeepAliveIntervalи обрывает подключение, если PONG не пришёл в течениеKeepAliveTimeout. - .NET 8: каждое ожидающее чтение из сокета ограничено
ReceiveIdleTimeout, который сбрасывается при получении каждого фрагмента. Простаивающие клиенты должны отправлять трафик приложения в пределах этого времени. Клиенты DarkWS по умолчанию отправляют текстовыйpingкаждые 30 секунд. Транспортные PONG не учитываются. Ожидание допуска в очередь выполнения не приостанавливает таймер чтения.
Оба клиента также обнаруживают «мёртвые» серверы с помощью собственного таймаута ping/pong.
Отправка и остановка
SendTimeoutохватывает ожидание блокировки отправки подключения и запись в сокет. По истечении сокет обрывается.- Рассылки используют более короткий
BroadcastSendTimeoutдля каждого получателя, поэтому медленный клиент не может задержать остальных. - При остановке подключение сразу удаляется из хранилища. Закрытие со стороны клиента
отменяет токены действий, аутентификации, её хуков и отправки ответов на команды.
ShutdownTimeout— единый дедлайн для незавершённых действий и команд, хуков закрытия и close handshake; после него сокет обрывается. - Callbacks, игнорирующие отмену, сохраняют свой scope и отложенные ресурсы подключения до завершения. Остановка всё равно ограничена дедлайном; запоздавшие результаты аутентификации и ответы отбрасываются. Нужные значения HTTP-контекста следует скопировать заранее: запрос обновления соединения уже может завершиться.
- Принятый сокет закрывается и освобождается также при ошибке первоначальной регистрации
сессии, включая исключения из её
IdилиGroups.
JSON
JsonOptions общий для всех обработчиков. По умолчанию свойства прикладных DTO
используют camelCase, а чтение не учитывает регистр. Имена полей конверта id,
action, data и error закреплены атрибутами JsonPropertyName и не меняются от
политики именования. Клиенты должны соблюдать схему DTO внутри data.
Конверт Redis использует независимые настройки сериализации.
Логирование
- Успешные действия и некорректные запросы клиентов логируются на уровне
Debug. - Непредвиденные сбои обработчиков и ошибки аутентификации при upgrade логируются как предупреждения.
- Сообщения исключений обработчиков попадают в лог, но никогда не отправляются клиентам.