You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django StreamingHttpResponse文本响应被缓冲问题排查求助

Django流式响应缓冲问题排查与解决

环境信息

  • Python 3.11.4
  • Django 4.2.5
  • 部署架构:Daphne(ASGI)+ Nginx反向代理,Redis、PostgreSQL默认配置

代码实现

后端Django视图

@csrf_exempt
def testPost(request):
    if request.method == 'POST':
        print('--- post')
        def gen1():
            for i in range(10):
                time.sleep(0.5)
                print(f'----- {i}')
                yield str(i)
        return StreamingHttpResponse(gen1(), content_type="text/plain", buffered=False)

前端JS请求代码

function testPost(messages, success, error) {
  const xhr = new XMLHttpRequest();
  xhr.open("POST", apiUrl);
  xhr.responseType = "text";
  xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  xhr.onprogress = (event) => {
    const partialResponse = xhr.responseText.substring(
      xhr.loadedBytes || 0,
      event.loaded
    );
    xhr.loadedBytes = event.loaded;
    success(partialResponse); // Handle the partial response
  };
  xhr.onload = () => {
    console.log('---loaded');
    success(xhr.responseText); // Handle the final response
  };
  xhr.onerror = error;
  const requestData = new URLSearchParams({
    messages: JSON.stringify(messages),
  }).toString();

  xhr.send(requestData);
}

// Usage
chatPost(messages, (partialResponse) => {
  console.log(partialResponse); // Handle the partial response
}, (error) => {
  console.error(error);
});

问题现象

服务器端依次输出0-9的日志,但前端需等待所有内容生成完成后才一次性收到完整字符串"0123456789",无法逐段接收流式内容。

已排除的可能原因

  • 网络延迟:测试30次迭代、1秒延迟场景下,网络延迟仅约80ms,可排除;
  • Nginx代理缓冲:已设置proxy_buffering off;
  • Nginx压缩:已关闭gzip;
  • Django响应缓冲:已设置StreamingHttpResponse的buffered=False;
  • 中间件影响:仅保留corsheaders中间件,问题仍存在。

排查与解决方案

1. 适配ASGI模式下的生成器输出

Daphne等ASGI服务器可能会合并小数据块,需确保每个生成的内容被立即推送。修改生成器,添加换行符并强制刷新:

@csrf_exempt
def testPost(request):
    if request.method == 'POST':
        print('--- post')
        def gen1():
            for i in range(10):
                time.sleep(0.5)
                print(f'----- {i}')
                # 添加换行符触发服务器推送,避免小数据块被合并
                chunk = str(i) + '\n'
                yield chunk
                # 强制刷新输出缓冲区
                import sys
                sys.stdout.flush()
        return StreamingHttpResponse(gen1(), content_type="text/plain", buffered=False)

2. 补充Nginx配置

即使关闭了proxy_buffering,仍需添加额外配置避免隐性缓冲:

location / {
    proxy_buffering off;
    proxy_cache off;
    proxy_set_header Connection "";
    proxy_http_version 1.1;
    # 限制缓冲区大小,确保小块数据不被滞留
    proxy_buffers 16 1k;
    proxy_buffer_size 1k;
}

3. 修复前端onprogress处理逻辑

原代码用字节数跟踪已加载内容,可能因文本编码导致匹配错误,改为跟踪字符数:

function testPost(messages, success, error) {
  const xhr = new XMLHttpRequest();
  xhr.open("POST", apiUrl);
  xhr.responseType = "text";
  xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  let processedLength = 0; // 跟踪已处理的字符数
  xhr.onprogress = (event) => {
    if (xhr.responseText) {
      const newContent = xhr.responseText.slice(processedLength);
      if (newContent) {
        success(newContent);
        processedLength = xhr.responseText.length;
      }
    }
  };
  xhr.onload = () => {
    console.log('---loaded');
    const remaining = xhr.responseText.slice(processedLength);
    if (remaining) success(remaining);
  };
  xhr.onerror = error;
  const requestData = new URLSearchParams({
    messages: JSON.stringify(messages),
  }).toString();

  xhr.send(requestData);
}

4. 验证Daphne直接访问

绕过Nginx,直接访问Daphne端口(如http://localhost:8000/testPost)测试流式响应是否正常:

  • 若正常,说明问题仍在Nginx配置细节;
  • 若仍异常,检查ASGI应用入口是否正确,确保无额外中间件拦截流式响应。

核心原理

流式响应的关键是每个数据块生成后立即推送至客户端,ASGI服务器、反向代理甚至生成器的小数据块合并,都会导致一次性接收。添加换行符是利用多数服务器的换行触发推送机制,强制刷新则确保内容不被滞留。

内容的提问来源于stack exchange,提问作者Weilory

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 18:13:32