bigywater.tech
Flask · 149

Server-Sent Events

Flask 怎么实现 SSE?浏览器断线后会怎样?经过 nginx 要注意什么?这篇用真实的 gunicorn、nginx 与 Chrome 的 EventSource,把线上格式、断线续传与缓冲逐项验证。

Last-Event-ID=3
浏览器断线重连时自动带上的请求头,值是最后收到的事件 id
933 → 12 ms
经过 nginx 时第一条事件到达的时间:没有与有 X-Accel-Buffering: no
readyState=0
连接断开后 EventSource 的状态:自动重连,不是关闭

线上格式

实测 · 本机执行输出
  状态 200 | Content-Type: text/event-stream; charset=utf-8 | Cache-Control: no-cache | Transfer-Encoding: chunked
  响应正文(原样):
    'id: 1'
    'data: {"n": 1}'
    ''
    'id: 2'
    'data: {"n": 2}'
    ''
    'id: 3'
    'data: {"n": 3}'
    ''
    ': keep-alive comment'
    ''
    ''

上一篇:长轮询;下一篇:WebSocket 与 Flask-SocketIO。

浏览器的自动重连

实测 · 本机执行输出
  页面收到的事件(id:数据): ['1:{"n": 1}', '2:{"n": 2}', '3:{"n": 3}', 'onerror(readyState=0)', '4:{"n": 4}', '5:{"n": 5}', '6:{"n": 6}', 'onerror(readyState=0)']
  服务端日志里的连接: ['connect Last-Event-ID=None', 'connect Last-Event-ID=3']

浏览器是本机的 Google Chrome 无头模式。

nginx 的缓冲

实测 · 本机执行输出
  没有 X-Accel-Buffering           数据到达的时间点(ms,按 100 ms 以上的间隔分组): [933]
  响应头 X-Accel-Buffering: no      数据到达的时间点(ms,按 100 ms 以上的间隔分组): [12, 323, 632, 939]

nginx 是自己起的实例(自签证书),与第 134 篇相同的配置。

占用线程

实测 · 本机执行输出
  8 个 SSE 连接在进行时,/ping 用了 726 ms(每个连接要 1 秒才结束)

分步演示

Server-Sent Events
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动。gunicorn gthread、nginx 1.31.6(自己的配置、非默认端口)、本机 Google Chrome(无头模式)
    • 没有验证:事件的 retry 字段、自定义 event 名在浏览器里的 addEventListener、多进程下事件的广播(需要 redis 发布订阅)、HTTP/2 下的连接数限制、认证(EventSource 不能自定义请求头)的处理
    京ICP备2026067502号-1京公网安备11010502063487号