英文:
Dialing TCP Error: Timeout or i/o Timeout after a while of high concurrency request
问题
我最近在使用valyala/fasthttp
开发一个高并发的HTTP客户端时遇到了一个问题:在前15000个请求中,客户端工作得很好,但之后越来越多的dial tcp4 127.0.0.1:80: i/o timeout
和dialing to the given TCP address timed out
错误发生。
以下是示例代码:
var Finished = 0
var Failed = 0
var Success = 0
func main() {
for i := 0; i < 1000; i++ {
go get()
}
start := time.Now().Unix()
for {
fmt.Printf("Rate: %.2f/s Success: %d, Failed: %d\n", float64(Success)/float64(time.Now().Unix()-start), Success, Failed)
time.Sleep(100 * time.Millisecond)
}
}
func get() {
ticker := time.NewTicker(time.Duration(100+rand.Intn(2900)) * time.Millisecond)
defer ticker.Stop()
client := &fasthttp.Client{
MaxConnsPerHost: 10000,
}
for {
req := &fasthttp.Request{}
req.SetRequestURI("http://127.0.0.1:80/require?number=10")
req.Header.SetMethod(fasthttp.MethodGet)
req.Header.SetConnectionClose()
res := &fasthttp.Response{}
err := client.DoTimeout(req, res, 5*time.Second)
if err != nil {
fmt.Println(err.Error())
Failed++
} else {
Success++
}
Finished++
client.CloseIdleConnections()
<-ticker.C
}
}
问题详情:
服务器使用labstack/echo/v4
构建,当客户端出现超时错误时,服务器没有任何错误,并且通过Postman
或者浏览器(如Chrome
)手动执行请求都能正常工作。
在前15000个请求中,客户端运行得非常好,但之后,越来越多的超时错误发生,输出的Rate
也在下降。我在Google和GitHub上搜索,发现这个问题可能是最合适的,但没有找到解决方案。
另一个小问题是...
你可能已经注意到,当客户端启动时,它会首先生成一些the server closed connection before returning the first response byte. Make sure the server returns 'Connection: close' response header before closing the connection
错误,然后在大约15000个请求后开始生成越来越多的超时错误。为什么在开始时会生成连接关闭的错误?
机器信息:
Macbook Pro 14 2021(Apple M1 Pro),16GB内存,运行macOS Monterey 12.4。
英文:
I recently run into a problem when I develope a high concurrency http client via valyala/fasthttp
: The client works fine in the first 15K~ requests but after that more and more dial tcp4 127.0.0.1:80: i/o timeout
and dialing to the given TCP address timed out
error occours.
Sample Code
var Finished = 0
var Failed = 0
var Success = 0
func main() {
for i := 0; i < 1000; i++ {
go get()
}
start := time.Now().Unix()
for {
fmt.Printf("Rate: %.2f/s Success: %d, Failed: %d\n", float64(Success)/float64(time.Now().Unix()-start), Success, Failed)
time.Sleep(100 * time.Millisecond)
}
}
func get() {
ticker := time.NewTicker(time.Duration(100+rand.Intn(2900)) * time.Millisecond)
defer ticker.Stop()
client := &fasthttp.Client{
MaxConnsPerHost: 10000,
}
for {
req := &fasthttp.Request{}
req.SetRequestURI("http://127.0.0.1:80/require?number=10")
req.Header.SetMethod(fasthttp.MethodGet)
req.Header.SetConnectionClose()
res := &fasthttp.Response{}
err := client.DoTimeout(req, res, 5*time.Second)
if err != nil {
fmt.Println(err.Error())
Failed++
} else {
Success++
}
Finished++
client.CloseIdleConnections()
<-ticker.C
}
}
Detail
The server is built on labstack/echo/v4
and when client got timeout error, the server didn't have any error, and manually perform the request via Postman
or Browser like Chrome
are works fine.
The client runs pretty well in the first 15K~ request, but after that, more and more timeout error occours and the output Rate
is decreasing. I seached for google and github and I found this issue may be the most suitable one, but didn't found a solution.
Another tiny problem...
As you can notice, when the client start, it will first generate some the server closed connection before returning the first response byte. Make sure the server returns 'Connection: close' response header before closing the connection
error, and then works fine till around 15K issues, and then start generating more and more timeout error.Why it would generate the Connection closed error in the begining?
Machine Info
Macbook Pro 14 2021 (Apple M1 Pro) with 16GB Ram and running macOS Monterey 12.4
答案1
得分: 0
所以基本上,如果你试图打开一个连接然后尽快关闭它,它不会像"连接#1使用一个端口然后立即返回",会有很多处理需要完成,所以如果你想同时发送多个请求,我认为最好尽可能地重用连接。
例如,在fasthttp
中:
req := fasthttp.AcquireRequest()
res := fasthttp.AcquireResponse()
defer fasthttp.ReleaseRequest(req)
defer fasthttp.ReleaseResponse(res)
// 然后在下面进行请求
英文:
So basically, If you trying to open a connection and then close it as soon as possibile, it's not going to be like "connection#1 use a port then immediately return it back", there gonna be lots of processing needs to be done, so If you want to send many request at the same time, I think it's better to reuse the connection as possible as you can.
For example, in fasthttp
:
req := fasthttp.AcquireRequest()
res := fasthttp.AcquireResponse()
defer fasthttp.ReleaseRequest(req)
defer fasthttp.ReleaseResponse(res)
// Then do the request below
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论