如果我不回应,身体会怎样?


98

在Go中,我有一些http响应,有时我忘了打电话:

resp.Body.Close()

在这种情况下会发生什么?会有内存泄漏吗?defer resp.Body.Close()获取响应对象后立即放入是否安全?

client := http.DefaultClient
resp, err := client.Do(req)
defer resp.Body.Close()
if err != nil {
    return nil, err
}

如果有错误,可能resp还是resp.Body无效怎么办?


可以在存在return时将resp.Body.Close()推迟到err!= nil后,因为当err不为nil时,它已经关闭了。另一方面,当请求成功时,主体需要显式关闭。
Vasantha Ganesh K

Answers:


110

在这种情况下会发生什么?会有内存泄漏吗?

这是资源泄漏。连接将不会被重用,并且可以保持打开状态,在这种情况下,文件描述符将不会被释放。

在获取响应对象之后立即放置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,则响应将包含一个非零的Body,希望用户将其关闭。如果未同时将主体读入EOF并关闭,则客户端的基础RoundTripper(通常为Transport)可能无法将与服务器的持久TCP连接重新用于后续的“保持活动”请求。


4
根据此链接,仍然有可能泄漏与您的代码的连接。在某些情况下,响应为非零,错误为非零。
mmcdole,2016年

13
@mmcdole:该帖子是错误的,并且不能保证它不会惊慌,因为对错误返回的任何响应都没有定义的状态。如果没有因错误而关闭主体,则说明它是一个错误,需要报告。您应该阅读官方的客户文档,该文档指出“出错时,任何响应都可以忽略”,而不是随意的博客文章。
JimB

2
@ del-boy:如果您希望客户端发出更多请求,则应尝试读取正文,以便可以重用连接。如果您不需要连接,那么就不用理会身体了。如果您阅读正文,请用包裹io.LimitReader。我通常使用一个很小的限制,因为如果请求太大,建立新连接会更快。
JimB

1
值得指出的是,这样做_, err := client.Do(req)还会导致文件描述符保持打开状态。因此,即使不关心响应是什么,仍然有必要将其分配给变量并关闭主体。
j boschiero

1
对于任何感兴趣的人,完整的文档都是(强调):“在出错时,任何Response都可以忽略。只有当CheckRedirect失败时才会出现带有non-nil错误的non-nil响应,即使返回的Response.Body已经关闭。”
nishanthshanmugham

15

如果Response.Body不使用Close()method关闭,则与fd相关的资源将不会被释放。这是资源泄漏。

闭幕 Response.Body

来自响应源

关闭主体是呼叫者的责任。

因此,没有终结器绑定到该对象,因此必须显式关闭它。

错误处理和延迟清除

出错时,任何响应都可以忽略。仅当CheckRedirect失败并且返回的Response.Body已关闭时,才会出现具有非nil错误的非nil响应。

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

4
您应注意,它们应在您的错误处理条件内返回。如果用户未返回错误处理,这将引起恐慌。
applewood '18

3

首先,描述符永远不会关闭,如上所述。

而且,golang将缓存连接(使用persistConnstruct包装)以重新使用它(如果DisableKeepAlives为false)。

在golang使用client.Do方法之后,go将运行goroutinereadLoop方法作为步骤之一。

因此,在golang http中transport.go,在方法中的req取消之前,pconn(persistConn struct)不会将a放入idleConn通道readLoop,而且goroutine(readLoopmethod)也将被阻塞,直到req被取消。

这是显示它的代码

如果您想了解更多,则需要查看该readLoop方法。


1

请参阅https://golang.org/src/net/http/client.go
“当err为nil时,resp始终包含非nil resp.Body。”

但是他们没有说err!= nil时,resp总是nil。他们继续说:
“如果未关闭resp.Body,则客户端的基础RoundTripper(通常是Transport)可能无法将与服务器的持久TCP连接重新用于后续的“保持活动”请求。”

因此,我通常已解决了以下问题:

client := http.DefaultClient
resp, err := client.Do(req)
if resp != nil {
   defer resp.Body.Close()
}
if err != nil {
    return nil, err 
}

3
这是不正确的,并且不能保证在发生错误时resp.Body不会显示为nil。
JimB

1
谢谢@JimB。文档中的措辞是“出错时,任何响应都可以忽略”。说“出现错误时,响应主体始终处于关闭状态”会更准确。
candita'7

1
不,因为通常没有要关闭的响应主体。如果您继续阅读文档中的该段落-“只有CheckRedirect失败,并且返回的Response.Body已经关闭,才会发生非零错误且非零错误。”
JimB
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.