英文:
golang http client and server can't maintain reliable connection
问题
我有一个非常基本的用例,一个HTTP客户端每隔5秒向HTTP服务器发送一个POST
请求。
客户端代码如下:
client := &http.Client{Timeout: 1 * time.Second}
...
for {
time.Sleep(5 * time.Second)
body := bytes.NewBuffer([]byte("foo"))
req, err := http.NewRequest(http.MethodPost, "http://localhost:8080/foo", body)
...
resp, err := client.Do(req)
...
_ = resp.Body.Close()
}
服务器代码如下:
srv := &http.Server{
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
Addr: ":9090",
Handler: mux,
}
大多数情况下,请求是成功的。但是很随机地,它们会失败,并显示不同的错误消息:
# 服务器似乎关闭了连接
Post http://localhost:8080/foo: read tcp 127.0.0.1:41312->127.0.0.1:9090: read: connection reset by peer
# 客户端在执行请求时不幸关闭了自己的连接
Post http://localhost:8080/foo: EOF
# 这是一种经常发生的情况,但会导致重试,而POST请求无法重试
Post http://localhost:8080/foo: http: server closed idle connection
我可以通过在请求中设置req.Close = true
来避免这个问题,或者在HTTP客户端中禁用keep-alive,或者将请求的频率从每5秒改为每秒执行。这似乎是一个时间问题。
我无法理解的是,即使这似乎是世界上最基本的任务,我仍然无法在两者之间保持连接,而不是为每个请求创建一个新的连接。
我漏掉了什么?
英文:
I have a very basic usecase of an http client making a POST
request to an http server periodically, every 5 seconds.
the client:
client := &http.Client{Timeout: 1 * time.Second}
...
for {
time.Sleep(5 * time.Second)
body := bytes.NewBuffer([]byte("foo"))
req, err := http.NewRequest(http.MethodPost, "http://localhost:8080/foo", body)
...
resp, err := client.Do(req)
...
_ = resp.Body.Close()
}
the server:
srv := &http.Server{
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
Addr: ":9090",
Handler: mux,
}
Most of the times the requests are successful. But very randomly they fail with different error message:
# the server seems to close the connection
Post http://localhost:8080/foo: read tcp 127.0.0.1:41312->127.0.0.1:9090: read: connection reset by peer
# the client seems to close its own connection unfortunately while performing a request
Post http://localhost:8080/foo: EOF
# this is something that tends to happen, but would result in a retry, which cant work with a POST
Post http://localhost:8080/foo: http: server closed idle connection
I can avoid this problem by closing the connection in the request with req.Close = true
, by disabling keep-alive in the http client, or executing the request every second, instead of every 5 seconds. It seems to be a timing issue.
I can not wrap my head around the fact that I simply cannot maintain a connection between the two without creating a new connection for every request, even though this seems to be the most basic task in the world.
What am I missing?
答案1
得分: 1
可能是因为你将服务器的ReadTimeout
设置为5秒,而这恰好等于请求之间的间隔时间,所以你遇到了竞态条件。根据服务器文档的说明:
Server struct {
...
// IdleTimeout是在启用keep-alive时等待下一个请求的最长时间。
// 如果IdleTimeout为零,则使用ReadTimeout的值。如果两者都为零,则没有超时。
IdleTimeout time.Duration
这意味着当客户端在休眠5秒后发送进一步的请求时,连接可能已经关闭,也可能未关闭。或者,它可能会被单方面关闭。因此会出现各种错误消息。
将服务器的IdleTimeout
值设置为大于5秒的值应该会有所帮助。
英文:
Probably you encounter race condition because you have set server ReadTimeout
to 5s and this is exactly equal to interval between requests. According to the Server documentation:
Server struct {
...
// IdleTimeout is the maximum amount of time to wait for the
// next request when keep-alives are enabled. If IdleTimeout
// is zero, the value of ReadTimeout is used. If both are
// zero, there is no timeout.
IdleTimeout time.Duration
This means that when the client sends further requests, after 5 seconds of sleep, the connection may or may not already be closed. Alternatively, it may be unilaterally closed. Hence the various error messages.
Setting Server IdleTimeout
value to something higher than 5s should help.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论