拨号TCP错误:在高并发请求一段时间后超时或I/O超时。

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

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 timeoutdialing 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 &lt; 1000; i++ {
		go get()
	}
	start := time.Now().Unix()
	for {
		fmt.Printf(&quot;Rate: %.2f/s Success: %d, Failed: %d\n&quot;, 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 := &amp;fasthttp.Client{
		MaxConnsPerHost: 10000,
	}
	for {
		req := &amp;fasthttp.Request{}
		req.SetRequestURI(&quot;http://127.0.0.1:80/require?number=10&quot;)
		req.Header.SetMethod(fasthttp.MethodGet)
		req.Header.SetConnectionClose()
		res := &amp;fasthttp.Response{}
		err := client.DoTimeout(req, res, 5*time.Second)
		if err != nil {
			fmt.Println(err.Error())
			Failed++
		} else {
			Success++
		}
		Finished++
		client.CloseIdleConnections()
		&lt;-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 &#39;Connection: close&#39; 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

huangapple
  • 本文由 发表于 2022年6月24日 10:39:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/72738441.html
匿名

发表评论

匿名网友

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

确定