bigywater.tech
Flask · 42

Streaming Response

大文件、实时日志、逐行输出怎么返回?生成器响应在线路上是什么样?为什么生成器里访问 request 会出错?客户端断开会发生什么?这篇起真实的 gunicorn 把这些都测一遍。

38 vs 338 MiB
300 MB 响应:流式 vs 一次性构造的 worker 峰值内存
chunked
生成器响应的传输方式(没有 Content-Length)
GeneratorExit
客户端断开时生成器收到的异常

流式响应的头与到达时间

实测 · 本机执行输出
  视图(摘自 app.py):
      1 | def slow():
      2 |     def gen():
      3 |         for i in range(5):
      4 |             yield f"chunk{i}\n"; time.sleep(0.2)
      5 |     return Response(gen(), mimetype="text/plain")
  /all(先在内存里拼好再返回):收到响应头用了 1.02 秒,全部收完 1.02 秒;响应头: {'Content-Length': '35'}
  响应头: {'Connection': 'close', 'Transfer-Encoding': 'chunked', 'Content-Type': 'text/plain; charset=utf-8'}
  /slow 每个 chunk 到达的时间(秒)与内容: [(0.0, 'chunk0'), (0.2, 'chunk1'), (0.4, 'chunk2'), (0.61, 'chunk3'), (0.81, 'chunk4')]

上一篇:自定义 Response Class;下一篇:send_file() 与 send_from_directory();流式 JSON:Streaming JSON;Server-Sent Events:Server-Sent Events。

chunked 的线路格式

实测 · 本机执行输出
  HTTP/1.1 200 OK\r\n
  Server: gunicorn\r\n
  Date: Fri, 02 Oct 2026 03:25:25 GMT\r\n
  Connection: close\r\n
  Transfer-Encoding: chunked\r\n
  Content-Type: text/plain; charset=utf-8\r\n
  \r\n
  7\r\n
  chunk0
  \r\n
  7\r\n
  chunk1
  \r\n
  7\r\n
  chunk2
  \r\n
  7\r\n
  chunk3
  \r\n
  7\r\n
  chunk4
  \r\n
  0\r\n
  \r\n

stream_with_context

实测 · 本机执行输出
  GET /ctx-bad   → HTTP/1.1 200 OK | 正文 'start;'
  GET /ctx-good  → HTTP/1.1 200 OK | 正文 'start;path=/ctx-good'
  服务器日志里的错误行: ['[2026-10-02 11:25:26 +0800] [45204] [ERROR] Error handling request', '    raise RuntimeError(unbound_message) from None']

内存对比

实测 · 本机执行输出
  流式(生成器,每块 1 MB)          峰值 RSS ≈ 38 MiB(基线 35 MiB)| curl:314572800 字节 0.074608s
  一次性 bytes(300 MB)        峰值 RSS ≈ 338 MiB(基线 35 MiB)| curl:314572800 字节 0.097791s

客户端中途断开

实测 · 本机执行输出
  curl --max-time 0.5(退出码 28 )之后服务器记录的内容: ['generator closed by server (client disconnected)', 'finally 执行了']

分步演示

流式响应
本步的对象

    本文用到的工具与边界

    • 本机 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 默认的同步 worker;ps 采样的 RSS 只是粗略估计
    • 没有验证:gthread/gevent worker 下的流式行为与并发、Nginx 反向代理的缓冲(proxy_buffering)对流式的影响(见第 174 篇)、HTTP/2 下的分块、gunicorn worker timeout 对长时间流的影响(见第 166 篇)
    京ICP备2026067502号-1京公网安备11010502063487号