bytes.buffer可安全池化因无状态且支持reset,而http.responsewriter绑定连接状态等不可重用字段,池化会导致数据错乱或panic。

直接复用 bytes.Buffer 和 json.Encoder 是最简单、见效最快的提升点,但必须配合 Reset() 和类型断言处理,否则会污染响应或 panic。
为什么只池化 bytes.Buffer 而不池化 http.ResponseWriter
http.ResponseWriter 是接口,且其底层实现(如 response 结构体)绑定了连接状态、header map、写入计数器等不可重用字段,强行池化会导致数据错乱或 panic。而 bytes.Buffer 是纯内存缓冲区,无外部依赖,Reset 后可安全复用。
- 常见错误:把整个
http.ResponseWriter放进 pool —— 编译可能通过,运行时必然出错 - 正确做法:只池化它内部可替换的组件,比如序列化用的
*bytes.Buffer或绑定后的*json.Encoder - 注意:池中对象没有所有权保证,GC 会随时清理;不能存任何请求上下文(如
r.URL.Path)
如何安全地池化 json.Encoder
json.Encoder 本身不带缓冲,必须绑定一个 io.Writer(通常是 *bytes.Buffer)才能使用。直接池化 json.Encoder 效果有限,因为每次都要重新设置 writer;更高效的是池化已绑定 buffer 的 encoder 实例。
- 必须在
Put前调用encoder.SetEscapeHTML(false)等配置重置,否则后续请求可能被上一次的 escape 设置影响 - 推荐组合:池化
*bytes.Buffer,每次Get后用json.NewEncoder(buf)新建 encoder —— 开销极小,且语义清晰 - 若坚持池化 encoder,New 函数里必须 new buffer 并 new encoder 绑定,
Put前需buf.Reset(),否则 buffer 内容残留
Get/Reset/Put 的顺序不能颠倒
典型误操作是 defer Put 但忘了 Reset(),或者 Reset() 放在 Put 之后——这会导致下一次 Get 拿到的 buffer 仍含旧数据,响应体混杂。
- 正确顺序:
buf := bufferPool.Get().(*bytes.Buffer)→buf.Reset()→ 使用 →bufferPool.Put(buf) - 不能省略
Reset():即使 buffer 刚被 GC 清理过,pool 也可能返回之前用过的实例 - 避免在 handler 中跨 goroutine 使用同一 buffer:比如起 goroutine 异步写入,主 goroutine 就已
Put回池 —— 数据竞争风险极高
最容易被忽略的是:sync.Pool 不保证对象一定被复用,也不保证复用时机。高并发下它确实能压低 GC 频率,但在 QPS 很低或突发流量后,池可能长期为空,New 函数调用次数并不少。别把它当成“万能缓存”,它只是帮你把临时分配从每次请求降到每 N 次请求。











