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
相关产品推荐
相关产品推荐

