英文:
What could happen if I don't close response.Body?
问题
在Go语言中,如果忘记调用resp.Body.Close()
会发生什么?会有内存泄漏吗?同时,在获取响应对象后立即使用defer resp.Body.Close()
放在代码中是否安全?
如果发生错误,resp
或resp.Body
可能为nil吗?
英文:
In Go, I have some http responses and I sometimes forget to call:
resp.Body.Close()
What happens in this case? will there be a memory leak? Also is it safe to put in defer resp.Body.Close()
immediately after getting the response object?
client := http.DefaultClient
resp, err := client.Do(req)
defer resp.Body.Close()
if err != nil {
return nil, err
}
What if there is an error, could resp
or resp.Body
be nil?
答案1
得分: 185
在这种情况下会发生什么?会有内存泄漏吗?
这是一个资源泄漏。连接不会被重新使用,可能会保持打开状态,这样文件描述符就不会被释放。
另外,在获取响应对象后立即使用defer resp.Body.Close()安全吗?
不安全。请按照文档中提供的示例,在检查错误后立即关闭它。
client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
根据http.Client
文档:
如果返回的错误为nil,则响应将包含一个非nil的Body,用户应该在使用完毕后关闭它。如果Body既没有被完全读取又没有被关闭,客户端的底层RoundTripper(通常是Transport)可能无法重用与服务器的持久TCP连接进行后续的“keep-alive”请求。
英文:
> What happens in this case? will there be a memory leak?
It's a resource leak. The connection won't be re-used, and can remain open in which case the file descriptor won't be freed.
> Also is it safe to put in defer resp.Body.Close() immediately after getting the response object?
No, follow the example provided in the documentation and close it immediately after checking the error.
client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
From the http.Client
documentation:
> If the returned error is nil, the Response will contain a non-nil Body which the user is expected to close. If the Body is not both read to EOF and closed, the Client's underlying RoundTripper (typically Transport) may not be able to re-use a persistent TCP connection to the server for a subsequent "keep-alive" request.
答案2
得分: 18
如果Response.Body
没有使用Close()
方法关闭,与fd相关联的资源将不会被释放。这是一个资源泄漏。
关闭Response.Body
根据响应源码:
关闭Body是调用者的责任。
因此,对象没有绑定最终器,必须显式关闭。
错误处理和延迟清理
在错误情况下,可以忽略任何响应。只有在CheckRedirect失败时,才会出现非nil的响应和非nil的错误,即使在这种情况下,返回的Response.Body已经关闭。
resp, err := http.Get("http://example.com/")
if err != nil {
// 处理非nil的错误
}
defer resp.Body.Close() // 仅在响应非nil时关闭body
英文:
If Response.Body
won't be closed with Close()
method than a resources associated with a fd won't be freed. This is a resource leak.
Closing Response.Body
From response source:
> It is the caller's responsibility to close Body.
So there is no finalizers bound to the object and it must be closed explicitly.
Error handling and deferred cleanups
> On error, any Response can be ignored. A non-nil Response with a non-nil error only occurs when CheckRedirect fails, and even then the returned Response.Body is already closed.
resp, err := http.Get("http://example.com/")
if err != nil {
// Handle error if error is non-nil
}
defer resp.Body.Close() // Close body only if response non-nil
答案3
得分: 6
首先,描述符永远不会关闭,就像上面提到的那样。
而且,如果DisableKeepAlives
为false,golang会缓存连接(使用persistConn
结构进行封装)以便重用。
在golang中,在使用client.Do
方法后,go会作为其中一步运行goroutine readLoop
方法。
因此,在golang的http transport.go
中,直到在readLoop
方法中取消请求,pconn(persistConn结构)
才会被放入idleConn
通道中,而且这个goroutine(readLoop
方法)会被阻塞,直到请求被取消。
这里是代码展示了这一点。
如果你想了解更多,你需要查看readLoop
方法。
英文:
At first the descriptor never closes, as things mentioned above.
And what's more, golang will cache the connection (using persistConn
struct to wrap) for reusing it, if DisableKeepAlives
is false.
In golang after use client.Do
method, go will run goroutine readLoop
method as one of the step.
So in golang http transport.go
, a pconn(persistConn struct)
won't be put into idleConn
channel until the req canceled in the readLoop
method, and also this goroutine(readLoop
method) will be blocked until the req canceled.
Here is the code showing it.
If you want to know more, you need to see the readLoop
method.
答案4
得分: 0
请注意,我是一个语言模型,我不能直接执行代码。我可以帮助你理解代码并提供翻译。根据你提供的代码,我可以为你提供以下翻译:
参见 https://golang.org/src/net/http/client.go
当 err 为 nil 时,resp 始终包含一个非 nil 的 resp.Body。
但是他们没有说当 err != nil 时,resp 总是 nil。他们接着说:
如果 resp.Body 没有关闭,Client 的底层 RoundTripper(通常是 Transport)可能无法重用与服务器的持久 TCP 连接进行后续的“keep-alive”请求。
所以我通常这样解决这个问题:
client := http.DefaultClient
resp, err := client.Do(req)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return nil, err
}
英文:
See https://golang.org/src/net/http/client.go
"When err is nil, resp always contains a non-nil resp.Body."
but they do not say when err != nil, resp always is nil. They go on to say:
"If resp.Body is not closed, the Client's underlying RoundTripper (typically Transport) may not be able to re-use a persistent TCP connection to the server for a subsequent "keep-alive" request."
So I have typically solved the issue like this:
client := http.DefaultClient
resp, err := client.Do(req)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return nil, err
}
答案5
得分: 0
一种选择是将后续的请求放入一个新的上下文中,这样你可以使用相同的变量名,而不用担心覆盖任何现有的变量,并且仍然可以正确关闭所有内容:
package main
import (
"bytes"
"net/http"
)
func main() {
b := new(bytes.Buffer)
// 第一个请求
r, e := http.Get("http://speedtest.atl.hivelocity.net")
if e != nil {
panic(e)
}
defer r.Body.Close()
b.ReadFrom(r.Body)
// 第二个请求
{
r, e := http.Get("http://speedtest.lax.hivelocity.net")
if e != nil {
panic(e)
}
defer r.Body.Close()
b.ReadFrom(r.Body)
}
// 打印结果
print(b.String())
}
英文:
One option is to put subsequest requests into a new context, that way you can
use same variable name if you want, without worrying about clobbering any
existing variable, and still closing everything:
package main
import (
"bytes"
"net/http"
)
func main() {
b := new(bytes.Buffer)
// request one
r, e := http.Get("http://speedtest.atl.hivelocity.net")
if e != nil {
panic(e)
}
defer r.Body.Close()
b.ReadFrom(r.Body)
// request two
{
r, e := http.Get("http://speedtest.lax.hivelocity.net")
if e != nil {
panic(e)
}
defer r.Body.Close()
b.ReadFrom(r.Body)
}
// print
print(b.String())
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论