What could happen if I don't close response.Body?

huangapple go评论84阅读模式
英文:

What could happen if I don't close response.Body?

问题

在Go语言中,如果忘记调用resp.Body.Close()会发生什么?会有内存泄漏吗?同时,在获取响应对象后立即使用defer resp.Body.Close()放在代码中是否安全?

如果发生错误,respresp.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())
}

huangapple
  • 本文由 发表于 2015年10月20日 21:49:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/33238518.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定