Real-Time Backend
Event Notifications
Grafana показывает CPU spike в реальном времени. Без polling'а, без WebSocket - только HTTP. Как?
- **Grafana** использует SSE для live-панелей: 10 млн+ дашбордов обновляются без единого polling-запроса
- **Slack** стримит события рабочего пространства через SSE: presence, typing, notifications - один поток на вкладку браузера
- **GitHub Actions** показывает live-логи деплоя через SSE: каждая строка лога появляется в браузере за 50-100мс
- **PagerDuty** доставляет alert-уведомления через SSE, сокращая MTTA с 4.5 мин до 47 сек
SSE в admin-панелях
Server-Sent Events (SSE) - это HTTP-протокол, при котором сервер держит соединение открытым и толкает события клиенту в виде текстового потока. В отличие от WebSocket, соединение однонаправленное: сервер пишет, клиент слушает.
Для admin-панелей это идеальный fit: нужно показывать новые заказы, ошибки деплоя, статусы задач - всё это read-only поток данных. Grafana использует SSE для live-обновления дашбордов без polling'а.
SSE автоматически реконнектится при обрыве - браузер повторяет запрос через 3 секунды по умолчанию. `Last-Event-ID` заголовок позволяет возобновить поток с нужного события.
- `Content-Type: text/event-stream` - обязателен, иначе браузер не распознает протокол
- Каждое событие заканчивается двойным `\n\n`
- Строки с `:` - это комментарии (heartbeat), они не триггерят onmessage
- Лимит браузера - 6 одновременных SSE-соединений на домен (HTTP/1.1)
Admin-панель показывает новые заказы в реальном времени через SSE. При обрыве сети браузер...
Типизация событий дашборда
В admin-панелях через один SSE-поток приходят события разных типов: новые пользователи, ошибки, транзакции, деплои. Поле `event:` в SSE-протоколе позволяет маршрутизировать их на клиенте без условий.
Slack использует этот паттерн в web-клиенте: один SSE-стрим несёт события messages, reactions, presence, notifications. На клиенте каждый Redux slice подписан на свой тип событий.
При использовании нескольких именованных событий (`event: order`) клиент должен использовать `addEventListener`, а не `onmessage` - последний срабатывает только для событий без поля `event:` или с `event: message`.
SSE-сервер отправляет `event: deploy\ndata: {...}\n\n`. На клиенте это событие перехватит...
Стриминг метрик
Monitoring dashboards (Grafana, Datadog, Kibana) стримят метрики с частотой 1-5 секунд. Проблема - не слать весь набор метрик, а только дельту изменений. CPU 45% → 46%: слать `{cpu: 46}`, а не весь объект.
Datadog обслуживает 26 000+ клиентов, каждый с десятками дашбордов. Оптимизация delta-diff снижает трафик SSE на 60-80% по сравнению с отправкой полного снимка метрик.
- Threshold для числовых метрик - не слать микроизменения < 0.5%
- Батчинг: собирать изменения за 1с и слать одним событием вместо N событий
- Compression: `Content-Encoding: gzip` снижает payload в 5-10x для JSON-метрик
- Backpressure: если клиент медленный - дропать старые события, не буферизировать
Monitoring dashboard получает метрики через SSE раз в секунду. CPU изменился с 45.1% до 45.2%. Что должен сделать сервер?
Alert Notifications
Alert-уведомления - критический путь: если CPU > 95% или error rate > 1%, это должно попасть к оператору за секунды. SSE идеален для push-уведомлений в браузер, но нужна гарантия доставки при реконнекте.
PagerDuty при падении сервиса фиксирует MTTA (Mean Time to Acknowledge) - среднее 4.5 минуты у команд без push-уведомлений vs 47 секунд с SSE/WebPush. Разница критична для SLA.
- Присваивать каждому алерту монотонный `id` - нужен для `Last-Event-ID`
- Буферизировать последние N алертов на сервере (RingBuffer)
- При реконнекте - воспроизвести пропущенные события до текущего момента
- Дедуплицировать алерты на клиенте по `id` - реконнект может доставить дубли
SSE надёжен сам по себе - если сервер отправил событие, клиент его получит
SSE - это best-effort транспорт. События отправленные во время разрыва теряются, если сервер не хранит буфер и не реализует Last-Event-ID replay
HTTP не гарантирует доставку на уровне приложения. При обрыве соединения данные в TCP-буфере теряются. Last-Event-ID + серверный буфер - единственный способ гарантировать at-least-once доставку для SSE
SSE-клиент получил алерты с id 1-10, затем соединение оборвалось. При реконнекте браузер отправит заголовок `Last-Event-ID: 10`. Что должен сделать сервер?
Итоги
- **SSE = однонаправленный HTTP-стрим**: сервер толкает, клиент слушает - идеально для read-only событий в admin-панелях
- **Типизация через `event:`**: один стрим несёт события разных типов, клиент маршрутизирует через `addEventListener`
- **Гарантия доставки**: серверный буфер + `Last-Event-ID` позволяют воспроизвести пропущенные события после реконнекта
Связанные темы
SSE - часть более широкого пространства real-time паттернов:
- WebSocket vs SSE — Альтернативный протокол для двунаправленного общения
- Push Notifications — SSE работает только в открытой вкладке, Web Push - альтернатива для фоновых уведомлений
- Message Queue — Алерты часто приходят из Kafka/RabbitMQ и проксируются через SSE в браузер
Вопросы для размышления
- Когда SSE предпочтительнее WebSocket, а когда наоборот?
- Как обработать 10 000 одновременных SSE-соединений на одном сервере Node.js?
- Что произойдёт с буфером алертов если сервер перезапустится - и как это исправить?