Go HTTP handler 默认支持 chunked transfer encoding,只需不设置 Content-Length 并流式写入;需显式调用 http.Flusher.Flush() 触发发送,且应监听 context.Done() 避免超时中断。

Go HTTP handler 怎么开启 chunked transfer 编码
Go 的 http.ResponseWriter 默认就支持 chunked transfer encoding——只要你不显式设置 Content-Length,且不提前关闭连接,底层会自动切块发送。不需要手动调用任何函数,也不用导入额外包。
常见错误是:在写响应前写了 w.Header().Set("Content-Length", "123"),或者用了 io.Copy 一次性把整个结果读进内存再写出去,这会强制 Go 回退到固定长度模式,chunked 就失效了。
- 确保没设置
Content-Length(包括间接设置,比如用http.ServeFile或某些中间件) - 用
w.Write()或json.NewEncoder(w).Encode()等流式写法,别先bytes.Buffer全攒好 - 如果用了
gzip.Handler,它默认兼容 chunked;但某些自定义压缩中间件可能缓冲整个 body,要检查
为什么 ResponseWriter.Write() 有时不立即发包
Go 的 HTTP server 内部有小缓冲区(通常 4KB 左右),Write() 调用只是把数据塞进这个缓冲区,并不保证立刻发到客户端。这对 chunked 来说很关键:你得“触发 flush”才能让当前 chunk 真的发出。
典型场景是长轮询、SSE 或实时日志流——用户卡在 loading 状态,实际数据早就 Write() 过了,就是没刷出去。
立即学习“go语言免费学习笔记(深入)”;
- 必须显式调用
if f, ok := w.(http.Flusher); ok { f.Flush() } - 不能假设
fmt.Fprintf(w, "...")后自动 flush;尤其在循环中每写一段就要 flush 一次 - 注意:如果 client 断开连接,
Flush()可能返回broken pipe错误,建议检查 err
streaming 场景下如何避免 context timeout 中断 chunk 发送
HTTP handler 默认绑定请求的 context.Context,而这个 context 在超时或 client 断开时会 cancel。一旦 cancel,后续的 Write() 或 Flush() 可能返回 context canceled,导致流提前终止。
这不是 chunked 协议问题,而是 Go 的 context 传播机制在起作用。你得主动感知并优雅退出,而不是等 panic 或静默失败。
- 在循环写 chunk 前,用
select { case 检查是否已取消 - 不要忽略
Write()和Flush()的 error 返回值;ctx.Err()是常见原因 - 如果业务允许,可考虑用
req.Context().WithTimeout()控制单个 chunk 处理时间,而非整个流
curl / browser 接收 chunked 响应时的常见表现
Chunked 响应本身没有特殊 header 标识(Transfer-Encoding: chunked 是 server 自动加的,client 不需要关心),但接收端行为会影响你调试效果:
-
curl -N必须加-N(no buffering)才能逐块打印;否则 curl 自己缓存直到 EOF - 浏览器 DevTools 的 Network 面板里,Response 标签页是空的,直到流结束;要看实时数据得用
console.log+fetch().body.getReader() - Postman 默认不流式解析,容易误判“接口没返回”,建议换
curl -v或写个简单 HTML 页面测试
真正难的不是开启 chunked,而是控制好 flush 时机、处理好 context 生命周期、以及验证 client 确实收到了每一块——这三处漏掉任一,都会让你以为“功能没生效”。










