英文:
Close response body multiple times after multiple requests in golang
问题
在这篇帖子中指出,应该关闭response.Body以避免资源泄漏。在http package godoc的概述示例中也有展示。
在我的测试代码中,我在同一个函数中多次使用resp, err := http.DefaultClient.Do(req)
发送多个请求来尝试一个API。这样做是不是一个不好的做法?在这种情况下,我是在每个请求后写defer resp.Body.Close()
,还是只写一次?
url := server.URL + "/ticket/add"
reader = strings.NewReader(`{"id": "test1", "detail": "test1"}`)
req, err := http.NewRequest("POST", url, reader)
assert.Nil(t, err)
resp, err := http.DefaultClient.Do(req)
assert.Nil(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusCreated, resp.StatusCode)
// 添加一个相同id的ticket
reader = strings.NewReader(`{"id": "test1"}`)
req, err = http.NewRequest("POST", url, reader)
assert.Nil(t, err)
resp, err = http.DefaultClient.Do(req)
assert.Nil(t, err)
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
相关问题,在服务器端即func(w http.ResponseWriter, r *http.Request)
内部,是否也需要关闭请求体(request body)?
英文:
In this post, it is pointed out that response.Body should be closed to avoid resource leak. It is also shown in the overview examples in http package godoc.
In my test code, I send multiple requests to try an API with
resp, err := http.DefaultClient.Do(req)
multiple times in the same function. Is this a bad practice? In this case, do I write defer resp.Body.Close()
after each of them, or just once?
url := server.URL + "/ticket/add"
reader = strings.NewReader(`{"id": "test1", "detail": "test1"}`)
req, err := http.NewRequest("POST", url, reader)
assert.Nil(t, err)
resp, err := http.DefaultClient.Do(req)
assert.Nil(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusCreated, resp.StatusCode)
// add a ticket with same id
reader = strings.NewReader(`{"id": "test1"}`)
req, err = http.NewRequest("POST", url, reader)
assert.Nil(t, err)
resp, err = http.DefaultClient.Do(req)
assert.Nil(t, err)
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
A related question, on the server side, i.e., inside the func(w http.ResponseWriter, r *http.Request)
, is it necessary to close the request body as well?
答案1
得分: 5
是的,你需要关闭两个响应。将一个调用推迟到resp.Body.Close
不会对另一个产生影响。在每种情况下,*http.Response
是不同的,它们都可以被推迟关闭。
在服务器端,你不需要关闭Request.Body
-- 根据http.Request
文档:
// 服务器将关闭请求体。ServeHTTP处理程序不需要关闭它。
英文:
Yes, you need to close both responses. Deferring one call to resp.Body.Close
does not somehow effect the other. The *http.Response
is different in each case, and they both can be deferred.
On the server side, you do not need to close the Request.Body
-- from the http.Request
documentation:
// The Server will close the request body. The ServeHTTP
// Handler does not need to.
答案2
得分: 0
不良实践
如果你没有重用resp
变量,那么很明显你正在处理不同的响应实例,每个实例都必须关闭。
> Do发送一个HTTP请求并返回一个HTTP响应(实例)...
因此,不良实践是不执行多个请求,而是重用同一个变量来处理多个响应。这会导致代码不明显,并产生永远不会被销毁的不可达对象。
延迟执行
> 延迟语句将一个函数调用推入列表中。保存的调用列表在包围函数返回后执行。
如果你计划了单个或多个带有对同一变量的引用的延迟执行,那么只有最后一个分配给该变量对象的方法将被执行(对于多个延迟执行,会执行多次)。
关闭Response.Body
根据Response
文档:
> 关闭Body是调用者的责任。
因此,通常你必须关闭每个Response.Body
。
垃圾回收和终结(编辑)
垃圾收集器调用绑定到收集对象终结器以关闭文件、连接和执行其他清理操作。默认情况下,Body
对象没有绑定终结器。
你改进后的代码片段:
// ...大量的代码
resp_one, err := http.DefaultClient.Do(req)
// 顺便说一句,`assert.Nil`只返回断言是否成功(布尔值),而不会终止测试。
if assert.Nil(t, err) == true {
defer resp_one.Body.Close()
}
// ...大量的代码
resp_two, err = http.DefaultClient.Do(req)
if assert.Nil(t, err) == true {
defer resp_two.Body.Close()
}
英文:
Bad practice
If you did not reuse resp
variable, it would be obvious that you are dealing with different response instances each of which must be closed.
> Do sends an HTTP request and returns an HTTP response (instance)...
So, the bad practice is not to do several requests but to reuse the same variable for several responses. It causes code unobviousness. And produces unreachable objects which never be finalized.
Deferred execution
> A defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns.
If you have scheduled single or several defer
red executions with reference to the same variable only the last assigned to the variable object's method will be executed (several times for several defer
s).
Closing Response.Body
From Response
documentation:
> It is the caller's responsibility to
close Body.
So, typically you must close each Response.Body
.
Garbage collecting and finalization (edit)
Garbage collector invokes bind to collecting objects finalizers to close files, connections and to do other cleanup actions. And there is no finalizers bind to Body
object by default.
Your improved snippet:
// ...a lot of code
resp_one, err := http.DefaultClient.Do(req)
// BTW, `assert.Nil` just returns whether the assertion was successful (bool) not terminates a test.
if assert.Nil(t, err) == true {
defer resp_one.Body.Close()
}
// ...a lot of code
resp_two, err = http.DefaultClient.Do(req)
if assert.Nil(t, err) == true {
defer resp_two.Body.Close()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论