go http服务器响应不立即发送因启用http/1.1缓冲,write()仅写入内存buffer,需flush()或handler返回才真正发送;sse需设text/event-stream类型、no-cache缓存控制及data:\n\n格式;timeouthandler会硬中断流式响应,应避免使用。

为什么 http.ResponseWriter 写完不立刻发给客户端?
因为默认启用了 HTTP/1.1 的响应缓冲,Write() 只是往内存 buffer 里塞数据,不到 Flush() 或 handler 返回,底层不会真正 send。流式响应失效的根源就在这儿。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 必须显式调用
Flusher().Flush(),且要先判断接口是否支持:f, ok := w.(http.Flusher); if !ok { return } - 别在
WriteHeader()前调Flush(),否则会触发隐式 200,后续再写 header 就 panic - 如果用了
gzip.Handler中间件(比如http.ListenAndServe默认没开,但反代或某些框架可能加了),它会禁用 flush —— 这是常见静默失败点
怎么让 Go HTTP server 支持 SSE(Server-Sent Events)?
SSE 不是协议魔法,只是约定:用 text/event-stream MIME 类型 + 特定字段格式 + 持久连接。Go 原生完全支持,但细节容易错。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 设置响应头:
w.Header().Set("Content-Type", "text/event-stream")和w.Header().Set("Cache-Control", "no-cache") - 每条消息以
data: ...\n\n结尾(注意两个换行),可选加id:、event:字段 - 连接不能自动断开,需保持 handler 不返回;但也要防客户端断连,建议在每次
Write()后检查w.(http.CloseNotifier).Done()(Go 1.8+ 已弃用,改用req.Context().Done())
http.TimeoutHandler 会中断流式响应吗?
会,而且是无差别硬中断。它不管你在 flush 还是正在写 data,超时就直接 close connection。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 流式接口绝不要套
http.TimeoutHandler—— 它和长连接天然是冲突的 - 改用 context 控制单次推送耗时(比如
time.AfterFunc配合 channel select),或把超时逻辑下沉到业务层 - 如果必须全局 timeout,至少用
http.Server.ReadTimeout/WriteTimeout,它们只管建连和首字节,不影响已建立的流
客户端收不到第一条数据?检查 Transfer-Encoding: chunked 是否被中间件吃掉了
Nginx、某些 CDN 或反向代理默认会缓存第一个 chunk,直到凑够一定大小或超时才透传。Go server 发了,但卡在中间。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 在 Go server 端开头写 2KB 左右的 padding(比如
w.Write(bytes.Repeat([]byte(" "), 2048))),骗过代理的 chunk 缓存策略 - Nginx 配置加:
proxy_buffering off;和chunked_transfer_encoding on; - 用 curl 测试时加
-N参数禁用 buffering,排除本地终端干扰
流式响应真正的复杂点不在 Go 代码本身,而在整个链路中任意一环的缓冲行为 —— 从 net/http 底层、中间件、反代、浏览器解析,全都有自己的“等一等”逻辑。调试时得一层层确认谁在攒包。










