Server-Sent Events (SSE)
Server-Sent Events (SSE)
Server-Sent Events (SSE, события от сервера) — это стандартный способ долго держать HTTP-соединение открытым и передавать через него текстовые события от сервера клиенту. Клиент получает их по мере появления, не ожидая завершения всей операции.
История
SSE вырос из практики веб-разработчиков, которым нужно было получать обновления с сервера без постоянных запросов из браузера. До появления удобного браузерного интерфейса разработчики применяли long polling (длинный опрос): браузер отправлял запрос, сервер отвечал, а затем браузер сразу повторял запрос. Это работало, но создавало лишние циклы запросов и усложняло поддержку соединения.
Браузерное API EventSource появилось в ходе стандартизации HTML5. Описание SSE входит в HTML Living Standard, который поддерживает WHATWG. Это не отдельный протокол транспортного уровня: события идут поверх обычного HTTP, обычно через HTTP/1.1 или HTTP/2. Формат и поведение определяются браузерным API и стандартом, а не специальным портом или отдельной сетью.
SSE стал распространённым решением для уведомлений, лент событий и вывода ответа по мере генерации. В современных браузерах API EventSource поддерживается широко, но устроен прежде всего для GET-запросов и не позволяет произвольно задать заголовки запроса. Для POST-запросов и особой авторизации часто используют fetch() и самостоятельно читают поток ответа.
Важные вехи — появление EventSource в браузерах, формализация формата событий в HTML и распространение HTTP/2, позволяющего эффективнее обслуживать множество одновременных запросов в рамках одного соединения. Точные даты внедрения отдельных браузерных реализаций различаются; SSE не следует приписывать одной компании или одной версии браузера.
Что это такое
SSE — однонаправленный поток: сервер отправляет события, клиент их принимает. Клиент не отправляет ответные сообщения по этому же каналу, как при двунаправленном WebSocket. Для действий пользователя браузер делает обычный HTTP-запрос, а SSE-соединение используется отдельно для получения обновлений.
Тело ответа — текст UTF-8, организованный в блоки. В блоке могут быть поля event, data, id и retry. Например, event: progress задаёт тип события, data: 42 содержит данные, а пустая строка завершает блок. Несколько строк data: объединяются. Комментарий, начинающийся с двоеточия, часто служит heartbeat (сигналом жизни), чтобы промежуточные узлы не считали соединение простаивающим.
Ответ обычно имеет тип text/event-stream. Сервер не должен сначала собирать весь вывод и только затем отправлять одним ответом: смысл SSE — последовательная доставка фрагментов. Браузерный EventSource автоматически переподключается после разрыва. Если сервер задаёт идентификатор события через id:, клиент при повторном подключении может сообщить его в Last-Event-ID; приложение тогда может попытаться продолжить поток с нужного места.
SSE часто выбирают для вывода генеративной модели, прогресса импорта, уведомлений и обновления состояния фоновой задачи. Однако стандарт не гарантирует доставку каждого события ровно один раз, не хранит историю и не задаёт бизнес-семантику. При переподключении серверу и приложению нужно договориться, как находить пропуски и повторы.
SSE vs WebSocket: SSE проще, работает как обычный HTTP-ответ и подходит, когда данные в основном идут сервер → клиент. WebSocket даёт постоянный двунаправленный канал, но требует иной модели соединения и более сложного управления. SSE vs polling: polling периодически спрашивает «что нового?», тогда как SSE позволяет серверу отправить событие сразу после появления.
Аналогии из жизни
- Табло отправлений. Вместо того чтобы пассажир снова и снова спрашивал о статусе поезда, табло обновляется при поступлении сведений. Это помогает понять инициативу сервера. Где ломается: табло обычно показывает общую картину многим людям, а HTTP-поток принадлежит клиенту и может оборваться при смене сети.
- Радиоэфир. Ведущий последовательно сообщает новости, а слушатель принимает их по мере выхода. Это похоже на поток, который нельзя перемотать, если вещатель не хранит архив. Где ломается: SSE допускает идентификаторы и переподключения, а радио обычно не воспроизводит пропущенное автоматически.
- Заказ в кафе. Экран показывает «принят», «готовится», «готов», и новая отметка появляется по мере изменения заказа. Где ломается: статусы — бизнес-состояние, которое можно запросить заново; SSE — только канал доставки и не определяет, какой статус правильный.
Как это работает
- Клиент открывает HTTP-запрос. С
EventSourceэто обычноGET /events; если нужны тело запроса или специальные заголовки, используютfetch()и потоковый reader. - Сервер отвечает успешным статусом и заголовком
Content-Type: text/event-stream; charset=utf-8. Ответ остаётся открытым. Также важны корректные CORS и авторизация, если клиент и сервер находятся на разных origin. - Сервер передаёт события в текстовом формате. Например:
event: progress
data: {"percent": 40}
id: task-7-12
Пустая строка завершает событие. До неё клиент может не передать его обработчику. Строка, начинающаяся с :, — комментарий, не создающий прикладного события:
: keep-alive
- Браузер разбирает блоки. Обработчик
messageполучает события без явно заданного типа, а именованные типы слушаются черезaddEventListener, напримерprogress. Полеdataприходит текстом; JSON нужно разобрать отдельно. - При сетевом обрыве
EventSourceпытается подключиться снова.retry: 3000может задать задержку переподключения в миллисекундах. Идентификатор вid:позволяет браузеру сообщить серверу последний полученный ID, но серверу всё равно нужно хранить события или уметь восстановить состояние.
На сервере принципиально важно сбрасывать буферы после записи доступного фрагмента. Иначе приложение уже сформировало событие, но клиент увидит его лишь в конце ответа. Прокси, CDN, веб-сервер или компрессия тоже могут накапливать данные. Для стриминга весь тракт — от источника события до кода браузера — должен пропускать фрагменты без нежелательной буферизации и не закрывать соединение преждевременно.
Ресурсы конечны: каждый активный поток использует соединение, память и иногда задачу на сервере. Нужны тайм-ауты с учётом долгих соединений, отмена работы при отключении клиента, ограничение подключений и периодические heartbeat-сообщения. В HTTP/1.1 браузер может ограничивать число соединений к одному origin; HTTP/2 уменьшает эту проблему мультиплексированием, но не отменяет серверных лимитов.
Где встречается в обычной жизни
- Веб-приложение показывает прогресс загрузки или обработки файла, получая новые значения от сервера.
- Лента уведомлений обновляется без ручной перезагрузки страницы.
- При генерации текста интерфейс показывает первые слова до завершения всей генерации; для этого часто используют SSE или похожий HTTP-поток.
- Панель статуса сборки получает сообщения «началась», «выполняется», «завершилась» по мере изменения задачи.
Где встречается в IT и бизнесе
- Ответы генеративных моделей: части текста передаются по мере создания, снижая воспринимаемое ожидание. Важна не только скорость модели, но и отсутствие буферизации в сервере, прокси и клиенте.
- Фоновые задания: экспорт отчёта, обработка видео или импорт данных публикуют ход выполнения без частого опроса API.
- Операционные панели: события обновляют состояние очередей, задач и системных предупреждений.
- Уведомления и ленты: приложение получает новые события, пока открыта вкладка; историю обычно получают отдельным API.
- Простые live-обновления: когда сервер сообщает изменения, но частые двусторонние сообщения не нужны, SSE может быть проще WebSocket.
Кто пользуется
SSE — веб-стандарт, а не продукт одного поставщика. Его используют браузерные приложения и серверы, которым нужно доставлять события клиенту через HTTP. Реализации и библиотеки доступны для популярных серверных платформ, а EventSource встроен в браузеры.
Точный масштаб использования отдельных компаний обычно не публикуется. Не стоит приписывать конкретную архитектуру компании без её технической публикации. Практический масштаб зависит от числа одновременных соединений, частоты и размера событий, лимитов прокси и модели выполнения сервера. Несколько тысяч открытых потоков могут быть посильны асинхронному серверу, но это не универсальная гарантия: измерять нужно конкретную конфигурацию.
Альтернативы и конкуренты
- Polling (опрос): плюс — простая модель и лёгкое повторное получение состояния; минус — задержка между запросами и лишняя нагрузка при частом опросе.
- Long polling: плюс — серверная инициатива без отдельного браузерного API; минус — повторные запросы и более сложное управление большим числом клиентов.
- WebSocket: плюс — двунаправленная связь и гибкий обмен сообщениями; минус — больше требований к управлению соединениями и протокольным состоянием.
- Web Push: плюс — уведомления могут приходить вне открытой страницы при участии браузерной инфраструктуры; минус — иная модель разрешений и доставки, не предназначенная для непрерывного текстового потока.
Когда НЕ стоит использовать
- Не выбирай SSE, если клиенту нужно часто отправлять данные серверу по постоянному каналу: WebSocket или обычные HTTP-запросы лучше выражают двустороннее взаимодействие.
- Не используй SSE как единственное хранилище важных событий. Соединение может оборваться, вкладка закрыться, сервер перезапуститься; историю и надёжную доставку нужно проектировать отдельно.
- Не открывай поток для каждого короткого обновления, если проще один раз запросить текущее состояние. Постоянные соединения расходуют ресурсы и требуют масштабирования.
Связанные понятия
- HTTP: прикладной протокол, поверх которого передаются SSE-события.
- WebSocket: протокол постоянного двунаправленного обмена между клиентом и сервером.
- Long polling: ожидание события внутри обычного HTTP-запроса с последующим повтором.
- Потоковая передача: доставка ответа частями, до его полного формирования.
- Heartbeat: периодический сигнал, подтверждающий, что соединение ещё активно.
- Обратное давление (backpressure): согласование скорости отправителя и получателя, чтобы медленный клиент не переполнял буферы.
Литература и источники
- WHATWG, HTML Living Standard, раздел о серверных событиях: https://html.spec.whatwg.org/multipage/server-sent-events.html
- MDN Web Docs, Using server-sent events: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
- MDN Web Docs, EventSource: https://developer.mozilla.org/en-US/docs/Web/API/EventSource
- WHATWG, Fetch Standard: https://fetch.spec.whatwg.org/
Где встретилось у меня
SSE всплыл при обсуждении доставки постепенно генерируемых ответов в интерфейсе и устранения буферизации на пути между сервером, прокси и клиентом. В том же контексте обсуждались авторизация сотрудников и проверка качества приложения; внутренние подробности здесь не приводятся.
Краткое резюме
- SSE — однонаправленный поток событий сервер → клиент поверх HTTP.
- Формат текстовый; пустая строка завершает событие, а
idиretryпомогают при переподключении. - Для немедленного отображения нужно исключить буферизацию на всём пути данных.
- SSE проще WebSocket для односторонних обновлений, но не является хранилищем и не гарантирует доставку.
- Используй его для прогресса, уведомлений и потокового вывода, а важное состояние сохраняй отдельно.