# Реальное время (/ru/docs/tutorials/realtime)

API реального времени держат соединение открытым и обмениваются сообщениями в обе стороны. В этом руководстве вы
понаблюдаете за потоком заказов демо-магазина по WebSocket и как за Server-Sent Events, затем поговорите со службой
поддержки магазина по Socket.IO и прочитаете температуры складов по MQTT — всё рядом друг с другом.

- **Время:** 20 минут.
- **Вам понадобится:** [рабочее пространство Alpenglow](/docs/tutorials#the-alpenglow-workspace) с выбранным
  **Без окружения**. Для Socket.IO и MQTT — [локальные серверы](/docs/tutorials#servers-on-your-mac-for-grpc-socketio-and-mqtt):
  выполните `bun run realtime` в `examples/servers`.
- **Вы узнаете:** про подключение, сохранённые сообщения, живой журнал, потоки событий, пространства имён и
  подтверждения, подписки, QoS и сохранённые (retained) сообщения.

## 1. WebSocket: поток заказов [#1-websocket-the-order-feed]

1. Откройте **Realtime › Order updates**. Его URL — `{{wsURL}}/orders`, то есть `wss://demo.echolot.dev/v1/orders`.
2. Нажмите **Соединить** (<kbd>⌘</kbd><kbd>R</kbd>). Кнопка превратится в **Отключиться**.
3. Вкладка **Сообщения** ответа заполняется: значок звена для открытого соединения, `hello` сервера, затем событие
   заказа каждые 1,5 секунды, например `{"event": "order.created", "order": "ord_9Tq3Vb", …}`. У отправленных сообщений
   стрелка вверх, у полученных — зелёная стрелка вниз. **Ещё** разворачивает длинное сообщение JSON.
4. На вкладке **Сообщения** запроса сохранённое сообщение **Subscribe** содержит
   `{"op":"subscribe","channel":"orders"}`. Нажмите его **Отправить**. Сервер отвечает
   `{"op": "subscribed", "channel": "orders"}`.
5. Введите в поле ввода внизу `{"id": 42, "text": "hello"}` и нажмите **Отправить** (<kbd>⇧</kbd><kbd>⌘</kbd><kbd>↩</kbd>).
   Сервер подтверждает его ответом `{"op": "ack", "id": 42}`. Нажмите **+**, чтобы сохранить сообщение на потом.
6. Сузьте журнал кнопками **Все**, **Отправлено** и **Получено** или введите `shipped` в **Фильтр сообщений**.
7. Нажмите **Отключиться**. Журнал заканчивается строкой **Closed by Echolot**.

Демо закрывает сеанс через три минуты (`1000 Demo sessions last 3 minutes`), а если вы отправите больше 20 сообщений
за десять секунд, закрывает его с кодом `1008`. Сохранённые сообщения с отмеченным **Отправлять в прогонах**
отправляются по порядку после подключения, когда запрос выполняется в Прогоне или инструментом командной строки.
Подробнее — в разделе [WebSocket и SSE](/docs/requests/websocket-and-sse).

<Shot id="tutorials-realtime-websocket" alt="Order updates подключён: слева сохранённые сообщения Subscribe и Ping, справа живой журнал отправленных и полученных сообщений" />

## 2. Server-Sent Events: тот же поток как стрим [#2-server-sent-events-the-same-feed-as-a-stream]

1. Откройте **Realtime › Order events**. Это обычный HTTP-запрос `GET {{baseURL}}/orders/events` с заголовком
   `Accept: text/event-stream`.
2. Нажмите **Отправить**. Ответ откроется на вкладке **События**: **Прослушивание… «Остановить» завершает его.**
3. Событие приходит каждую секунду, каждое в своей карточке с номером и названием, например **1. order.created**, а под
   ним **id 1**. После 20 событий сервер отправляет `end` и закрывает поток.
4. Чтобы остановиться раньше, нажмите **Отменить** (<kbd>⌘</kbd><kbd>.</kbd>).

Чтобы продолжить после какого-то события, добавьте заголовок `Last-Event-ID` с последним увиденным id, скажем `15`, и
отправьте снова. Сервер пришлёт 16–20. Если ввести `Last-Event-ID` на вкладке **Заголовки**, в качестве значения
предлагается `{{lastEventId}}`; задайте этой переменной значение или введите число.

## 3. Socket.IO: служба поддержки [#3-socketio-the-support-desk]

Сначала запустите локальные серверы (`bun run realtime`). Они выведут
`Alpenglow realtime: Socket.IO on http://localhost:3001/support, MQTT on mqtt://localhost:1883`.

1. Откройте **Realtime › Support chat**. Его URL — `{{realtimeURL}}`, `http://localhost:3001`. На его вкладке **События**
   **Пространство имён** — `/support`; меню метода в строке URL задаёт версию **Сервер** (Socket.IO 4).
2. Нажмите **Соединить**. Придут три события: `welcome` с именем агента и очередью, `typing`, затем `message`:
   «Grüezi! How can I help with your order?»
3. Сохранённое событие **Say hello** отправляет `message` с `{"text":"Grüezi!"}`. Отметьте его флажок **Ack**, чтобы от
   сервера потребовалось подтверждение, и нажмите **Отправить**.
4. В журнале видно подтверждение `{"id": "msg_31", "delivered": true}`, а чуть позже — ответ Хайди:
   «Your Matterhorn 2P ships tomorrow from Bern.»
5. Чтобы отправить несохранённое событие, введите его имя в поле **event** поля ввода, а аргументы — как JSON, и
   нажмите **Отправить** (<kbd>⇧</kbd><kbd>⌘</kbd><kbd>↩</kbd>).

В каждой строке журнала указаны пространство имён, событие и номер подтверждения, если он есть. **Фильтр событий** сужает
журнал. Если сервер просит подтвердить одно из своих событий, в этой строке появляется кнопка **Подтвердить**. Данные
авторизации, например `{"token": "{{token}}"}`, указываются в поле **Авторизация** на вкладке **События**. См.
[Socket.IO](/docs/requests/socket-io).

## 4. MQTT: датчики складов [#4-mqtt-warehouse-sensors]

Те же локальные серверы запускают брокер MQTT на `localhost:1883`, который публикует температуры трёх складов каждые
1,5 секунды.

1. Откройте **Realtime › Warehouse sensors**. Его URL — `mqtt://{{mqttHost}}`, а **Протокол** в меню метода —
   **MQTT 3.1.1**.
2. На его вкладке **Подписка** есть подписка `warehouses/+/temperature` с **QoS 1** и отмеченным **Подписаться при подключении**. `+` соответствует одному уровню топика.
3. Нажмите **Соединить**. Приходят сообщения для `warehouses/zurich/temperature`, `warehouses/bern/temperature` и
   `warehouses/chur/temperature`, например `{"celsius": 6.1, "humidity": 44, …}`.
4. Меню **Все топики** позволяет показать только один склад.
5. В поле ввода **Подписка** введите `warehouses/status`, оставьте **QoS 0** и нажмите **Подписка**. Сразу приходит одно
   сообщение, `{"online": 3}`, с пометкой «сохранено» (retained): брокер сохранил его для всех, кто подпишется позже.
6. На вкладке **Публикация** задайте топик `warehouses/bern/door`, содержимое `{"open": true}` и нажмите **Публикация**
   (<kbd>⇧</kbd><kbd>⌘</kbd><kbd>↩</kbd>). Подпишитесь на `warehouses/#`, чтобы увидеть, как оно вернётся: `#`
   соответствует всем уровням ниже.

Вкладка **Подключение** содержит **ID клиента**, настройку чистого сеанса, **Keep alive** и завещание (will). Имя
пользователя и пароль брокера вводятся на вкладке **Авторизация** как авторизация Basic; храните пароль в секретной
переменной. Инструмент командной строки тоже говорит на MQTT:

```sh
echolot mqtt subscribe mqtt://localhost 'warehouses/#' --count 5
```

См. [MQTT](/docs/requests/mqtt) и [`echolot mqtt`](/docs/cli/mqtt).

## 5. Рядом друг с другом [#5-side-by-side]

У каждого запроса своя вкладка. Оставьте **Order updates**, **Support chat** и **Warehouse sensors** открытыми в трёх
вкладках и подключите каждый: все соединения остаются открытыми, пока вы переключаетесь между ними. Когда сеанс
закрывается, Echolot сохраняет его в Истории вместе со всем журналом сообщений, так что позже его можно перечитать
(<kbd>⌘</kbd><kbd>Y</kbd>).

Те же локальные серверы обслуживают и gRPC: выполните `bun run grpc` и отправьте запросы из **Inventory**. **Watch stock** передаёт потоком десяток уровней запасов и заканчивается. См. [gRPC](/docs/requests/grpc).

## Дальше [#next-steps]

- [WebSocket и SSE](/docs/requests/websocket-and-sse): подпротоколы, бинарные сообщения, проверки сеансов.
- [Socket.IO](/docs/requests/socket-io) и [MQTT](/docs/requests/mqtt): все настройки.
- [Тестирование API и запуск в CI](/docs/tutorials/test-in-ci): сеансы выполняются и в Прогоне, отправляя свои
  сохранённые сообщения.
