为何检查完HTTP请求体r.Body后必须关闭?不关闭会有何影响?
为什么必须关闭HTTP请求的r.Body?
这个问题问得特别实在——很多刚上手Go HTTP开发的同学都会忽略r.Body的关闭操作,这里面其实藏着不少容易踩的坑,咱们慢慢说清楚。
不关闭r.Body会有什么后果?
- 连接池资源浪费,性能暴跌:Go的HTTP客户端和服务器端都默认使用连接池来复用TCP连接。如果
r.Body没被关闭,这个连接会被标记为“正在使用”,没法放回连接池里重复利用。高并发场景下,很快就会因为新建连接过多导致系统资源耗尽,服务响应变慢甚至直接挂掉。 - 内存泄漏风险:如果请求体比较大(比如上传文件、提交大表单),未关闭的
r.Body会让内存里的缓冲区一直被占用,GC没法及时回收。长期运行下来,服务的内存占用会越来越高,最终引发OOM(内存溢出)。 - 服务器端资源闲置:如果服务器这边没关闭
r.Body,即使已经处理完请求,底层的连接资源也没法及时释放,会一直占用服务器的文件描述符和内存,拖慢整体服务的处理能力。
为什么一定要关闭r.Body?
- 首先,
r.Body实现了io.ReadCloser接口——Go语言里有个不成文的规则:只要是实现了Closer接口的资源,用完就必须关闭,这是资源管理的基本准则,和你打开文件后要关闭是一个道理。 - 不管你有没有把
r.Body的内容读完,都得关闭它。比如有时候json.Decoder成功解析了请求体,但可能请求体里还有剩余的字节没被读取,这时候关闭r.Body会确保这些剩余资源被彻底释放。 - 还有个容易踩的坑:关闭操作一定要尽早放!像你代码里把
defer r.Body.Close()放在Decode之后,如果Decode出错直接return,那这个defer根本不会执行,r.Body就没被关闭。正确的做法是把defer放在函数最开头,确保无论请求处理成功还是失败,都会执行关闭操作。
举个修正后的代码例子:
func createFeedback(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() // 放在函数开头,保证任何分支都会执行关闭 // ... 初始化逻辑 ... f := feedback.New() if err := json.NewDecoder(r.Body).Decode(f); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } // ... 后续业务逻辑 ... }
简单总结一下:关闭r.Body是Go HTTP开发的必备最佳实践,核心就是为了避免资源泄漏、保证连接池的高效复用,别因为偷懒或者疏忽给自己挖了性能或稳定性的坑。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

