使用以下客户端代码(并在此主机上的8088端口上启动一个侦听web服务器),我很少能够获得超过23000个访问,然后client.Get()
就会出现以下错误:
panic: Get http://localhost:8088/: dial tcp 127.0.0.1:8088: can't assign requested address
奇怪的是,如果我增加计时器延迟(即从毫秒级别增加到微秒级别),需要更多的请求才会出现错误,可能需要170,000或更多次请求。
通过查看网络流量,每个客户端连接只使用了几次就断开连接(即客户端发送FIN)。因此很明显它正在创建许多TCP连接并且溢出了套接字表。鉴于Golang HTTP文档中默认启用了keepalives,我不希望出现这种情况。内核跟踪显示,在关闭之前基础套接字没有发出任何错误(除了EAGAIN,这是预期的,并不总是在套接字关闭之前发生)。
这是在OSX(14.4.0)上使用Go 1.4.2进行的。为什么客户端连接不能一直重用呢?
package main
import (
"io/ioutil"
"net/http"
"runtime"
"sync"
"time"
)
var reqnum = 0
func hit(client *http.Client) {
resp, err := client.Get("http://localhost:8088/")
if err != nil {
println(reqnum)
panic(err)
}
defer resp.Body.Close()
_, err = ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
reqnum++ // not thread safe, but shouldn't cause errors.
}
func main() {
var wg sync.WaitGroup
runtime.GOMAXPROCS(runtime.NumCPU())
client := &http.Client{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
ticker := time.NewTicker(time.Microsecond * 1000)
for j := 0; j < 120000; j++ {
<-ticker.C
hit(client)
}
ticker.Stop()
}()
}
wg.Wait()
}
MaxIdleConnsPerHost
。这个限制是针对每个Client
的,并且你在十个工作线程中共享一个。这也可以解释为什么有时候非常快的计时器会完成测试:如果你更快地让连接重新工作1000倍,那么就不太可能有3个以上的连接处于空闲状态。一个简单的测试是人为增加最大连接数或者每个工作线程的客户端是否会改变连接行为。 - undefined