7.4 KiB
События, отправляемые сервером (SSE)
Вы можете передавать данные потоком клиенту, используя Server-Sent Events (SSE).
Это похоже на Стриминг JSON Lines, но использует формат text/event-stream, который нативно поддерживается браузерами через EventSource API.
/// info | Информация
Добавлено в FastAPI 0.135.0.
///
Что такое Server-Sent Events?
SSE — это стандарт для потоковой передачи данных с сервера на клиента по HTTP.
Каждое событие — это небольшой текстовый блок с «полями», такими как data, event, id и retry, разделёнными пустыми строками.
Это выглядит так:
data: {"name": "Portal Gun", "price": 999.99}
data: {"name": "Plumbus", "price": 32.99}
SSE часто используют для стриминга ответов ИИ в чатах, живых уведомлений, логов и наблюдаемости, а также в других случаях, когда сервер «проталкивает» обновления клиенту.
/// tip | Совет
Если вам нужно стримить бинарные данные, например видео или аудио, посмотрите расширенное руководство: Stream Data.
///
Стриминг SSE с FastAPI
Чтобы стримить SSE с FastAPI, используйте yield в своей функции-обработчике пути и укажите response_class=EventSourceResponse.
Импортируйте EventSourceResponse из fastapi.sse:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[4,22] *}
Каждый возвращаемый через yield элемент кодируется как JSON и отправляется в поле data: события SSE.
Если вы объявите тип возврата как AsyncIterable[Item], FastAPI будет использовать его, чтобы выполнить валидацию, добавить документацию и сериализовать данные с помощью Pydantic.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[10:12,23] *}
/// tip | Совет
Так как Pydantic будет сериализовать это на стороне Rust, вы получите значительно более высокую производительность, чем если не объявите тип возврата.
///
Несинхронные функции-обработчики пути
Вы также можете использовать обычные функции def (без async) и применять yield тем же образом.
FastAPI проследит, чтобы выполнение прошло корректно и не блокировало цикл событий.
Так как в этом случае функция не async, правильным типом возврата будет Iterable[Item]:
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[28:31] hl[29] *}
Без объявленного типа возврата
Вы также можете опустить тип возврата. FastAPI использует jsonable_encoder для преобразования данных и их отправки.
{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[34:37] hl[35] *}
ServerSentEvent
Если вам нужно задать поля SSE, такие как event, id, retry или comment, вы можете возвращать через yield объекты ServerSentEvent вместо обычных данных.
Импортируйте ServerSentEvent из fastapi.sse:
{* ../../docs_src/server_sent_events/tutorial002_py310.py hl[4,26] *}
Поле data всегда кодируется как JSON. Вы можете передавать любое значение, сериализуемое в JSON, включая Pydantic-модели.
Необработанные данные
Если нужно отправлять данные без JSON-кодирования, используйте raw_data вместо data.
Это полезно для отправки заранее отформатированного текста, строк логов или специальных значений «сентинель», например [DONE].
{* ../../docs_src/server_sent_events/tutorial003_py310.py hl[17] *}
/// note | Примечание
data и raw_data взаимно исключают друг друга. В каждом ServerSentEvent можно задать только одно из них.
///
Возобновление с Last-Event-ID
Когда браузер переподключается после обрыва соединения, он отправляет последний полученный id в HTTP-заголовке Last-Event-ID.
Вы можете прочитать его как параметр заголовка и использовать, чтобы возобновить поток с того места, где клиент остановился:
{* ../../docs_src/server_sent_events/tutorial004_py310.py hl[25,27,31] *}
SSE с POST
SSE работает с любым HTTP-методом, не только с GET.
Это полезно для таких протоколов, как MCP, которые стримят SSE по POST:
{* ../../docs_src/server_sent_events/tutorial005_py310.py hl[14] *}
Технические детали
FastAPI из коробки реализует некоторые лучшие практики для SSE.
- Отправлять комментарий «ping» для поддержания соединения («keep alive») каждые 15 секунд, когда нет сообщений, чтобы предотвратить закрытие соединения некоторыми прокси, как рекомендовано в HTML specification: Server-Sent Events.
- Устанавливать заголовок
Cache-Control: no-cache, чтобы предотвратить кэширование потока. - Устанавливать специальный заголовок
X-Accel-Buffering: no, чтобы предотвратить буферизацию в некоторых прокси, например Nginx.
Вам не нужно ничего настраивать, это работает из коробки. 🤓