在Go的HTTP包中,当你从`res.Body`中读取完数据后,为什么需要关闭它呢?

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

Go HTTP package: why do I need to close res.Body after reading from it?

问题

请参考以下示例链接:https://pkg.go.dev/net/http#example-Get。下面是代码片段:

func main() {
	res, err := http.Get("http://www.google.com/robots.txt")
	if err != nil {
		log.Fatal(err)
	}
	body, err := io.ReadAll(res.Body)
	res.Body.Close() // 为什么要关闭!?
	if res.StatusCode > 299 {
		log.Fatalf("Response failed with status code: %d and\nbody: %s\n", res.StatusCode, body)
	}
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s", body)
}

我的问题是:为什么我需要在第7行关闭res.Body.Close()。我没有打开它。我在https://stackoverflow.com/questions/33238518/what-could-happen-if-i-dont-close-response-body中看到了解释,我理解文档是这样说的。

是否有更好的解决方法?

我认为这违反了开放/关闭原则。我认为如果ReadAll打开了流,它应该关闭它。在Go中通常是这样做的吗?

英文:

Please see the example here https://pkg.go.dev/net/http#example-Get. The snipped below as well:

func main() {
	res, err := http.Get("http://www.google.com/robots.txt")
	if err != nil {
		log.Fatal(err)
	}
	body, err := io.ReadAll(res.Body)
	res.Body.Close() // Why!?
	if res.StatusCode > 299 {
		log.Fatalf("Response failed with status code: %d and\nbody: %s\n", res.StatusCode, body)
	}
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s", body)
}

My question is: Why do I need to close the res.Body.Close() in line no 7. I did not open it. I see the explanation in https://stackoverflow.com/questions/33238518/what-could-happen-if-i-dont-close-response-body and I understand that documentation says so.

Could there be a better way of solving this?

I think this is breaking the open/close principle. I think if ReadAll opened the steam it should close it. Is this usually how those things are done in Go?

答案1

得分: 6

根据http.ClientDo()文档:

...如果未对Body进行EOF读取和关闭,Client的底层RoundTripper(通常是Transport)可能无法重用与服务器的后续“keep-alive”请求的持久TCP连接。

简而言之,如果不遵循这个建议,你的程序中将会积累“死”http连接,因为请求被执行后没有被清理或重用。随着时间的推移,你的程序性能将会下降,并且根据生成的连接数量,可能会耗尽处理网络连接的有限资源,如文件描述符。

英文:

From the http.Client Do() docs:

> ... 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.

In short, if you don't follow this advice, you will accumulate "dead" http connections in your program, as requests are executed - but never cleaned up and/or reused. Your program's performance will degrade overtime - and depending on how many connections are generated - you may exhaust finite resources like file-descriptors that handle network connections.

答案2

得分: 1

请看一下http包中的Get函数,

func Get(url string) (resp *Response, err error) {
	return DefaultClient.Get(url)
}

它返回一个指向Response的指针,Response是一个包含Body的结构体,

type Response struct {
        . 
        .
        .
	Body io.ReadCloser
        .
        .
}

深入研究io包的源代码,你会发现ReadCloser涉及到io.Reader和io.Writer。参见这里

关闭一个打开的Reader会终止一个等待的通道,并且底层的goroutine会被关闭。保留太多这样的开放通道会阻塞主机操作系统分配给运行程序的空闲进程,通常限制为一定数量的进程(可以更改为全部消耗)。

英文:

Have a look at Get func in http package,

func Get(url string) (resp *Response, err error) {
	return DefaultClient.Get(url)
}

it returns a pointer to Response which is a struct containing Body

type Response struct {
        . 
        .
        .
	Body io.ReadCloser
        .
        .
}

dig through the io package source code, you will find out that ReadCloser involves io.Reader and io.Writer. See this

Closing an open Reader would terminate a waiting channel and the underlying goroutine would be closed. Leaving too many of these open channels would block the host os to allocate free processes to the running program which is normally limited to consume a certain amount of processes (it could be altered to devour whole).

答案3

得分: 1

你所有问题的答案基本上是:因为文档是这样说的。这就是文档的好处。有些事情对你来说可能是“显而易见”的,有些则不是。所以你要阅读文档并按照其中的指示进行操作。

英文:

The answer to all your questions basically is: Because the documentation says so. That's what documentation is good for. Some things might be "obvious" to you, some not. That's why you read the documentation and follow it.

huangapple
  • 本文由 发表于 2021年10月19日 06:20:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/69623172.html
匿名

发表评论

匿名网友

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

确定