Практикум — WebSocket и события заказов
Практикум, шаг 7 из 8. Живые уведомления на orders-api. Теория — WebSockets в 8.05.
Зачем WebSocket здесь
После POST /api/v1/orders клиенту нужно узнать о смене статуса (reserved → confirmed) без polling. orders-api держит канал ws://localhost:5200/ws/orders и рассылает события подписчикам того же пользователя.
Формат сообщений (прикладной протокол)
Все кадры — текстовый JSON (UTF-8). Версия протокола в каждом сообщении:
{
"v": 1,
"type": "order.status_changed",
"payload": {
"orderId": "ord_x9y8",
"status": "confirmed",
"at": "2026-05-27T10:20:00Z"
}
}
| type | Направление | Смысл |
|---|---|---|
ping | клиент → сервер | keep-alive |
pong | сервер → клиент | ответ на ping |
order.status_changed | сервер → клиент | смена статуса заказа |
error | сервер → клиент | { "code": "unauthorized", "message": "..." } |
Hub в ASP.NET Core
Services/OrderWebSocketHub.cs:
Код ITЗагрузка примера кода…
OrderEventPublisher вызывает BroadcastToUserAsync после Confirm / Cancel.
Middleware маршрута
Program.cs:
Код ITЗагрузка примера кода…
JWT на handshake — через ?access_token= (см. шаг 6) или заголовок Authorization там, где прокси его не срезает.
Проверка в браузерной консоли
const token = "…"; // из POST /api/v1/auth/token
const ws = new WebSocket(`ws://localhost:5200/ws/orders?access_token=${token}`);
ws.onmessage = (e) => console.log(JSON.parse(e.data));
ws.onopen = () => ws.send(JSON.stringify({ v: 1, type: "ping" }));
Создайте заказ во второй вкладке Postman — в консоли придёт order.status_changed.
Масштабирование
In-memory ConcurrentBag работает на одном экземпляре. При горизонтальном масштабировании:
- Redis pub/sub или SignalR backplane для fan-out;
- sticky sessions на балансировщике как временная мера;
- события как Kafka/RabbitMQ — если нужна история и гарантия доставки.
Для практикума достаточно одного процесса dotnet run.
Переподключение клиента
Браузер и Postman периодически рвут WS. Рекомендуемый алгоритм на клиенте:
- При
onclose— экспоненциальная задержка (1 с, 2 с, 4 с … до 30 с). - После
onopen—GET /api/v1/orders/{lastKnownId}для сверки статуса. - Не полагаться только на пропущенные WS-сообщения при долгом офлайне.
Коды закрытия (RFC 6455):
| Код | Смысл в OrderDesk |
|---|---|
1000 | Нормальное закрытие сессии |
1008 | Policy violation (просрочен JWT) |
1011 | Внутренняя ошибка сервера |
Сверка с песочницей
Кнопка WS ping на intro отправляет { "type": "ping" } и показывает pong. После POST confirm в ручном режиме в ленте появится order.status_changed — тот же JSON, что ожидается в Postman WebSocket.
Следующий шаг
Соберём полный сценарий в Postman: шаг 8.