Reputation: 767
I am basically making a health check crawler to a huge list of domains. I have a Golang script that creates ~256 routines that make requests to the list of domains. I am using the same client with the following transport configuration:
# init func
this.client = &http.Client{
Transport: &http.Transport{
ForceAttemptHTTP2: true,
TLSHandshakeTimeout: TLSHandShakeTimeout,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
MaxConnsPerHost: -1,
DisableKeepAlives: true,
},
Timeout: RequestTimeout,
}
...
# crawler func
req, err := http.NewRequestWithContext(this.ctx, "GET", opts.Url, nil)
if err != nil {
return nil, errors.Wrap(err, "failed to create request")
}
res, err := this.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
...
I ran netstat -anp | wc -l
and can see over 2000+ connections with TIME_WAIT.
Upvotes: 0
Views: 1870
Reputation: 48366
The default number of goroutines per host for http.Client
is 2. One is for the receiver and the other for the sender. So for thousands of domains, there could be thousands of goroutines here.
As the DisableKeepAlives
is set to true
, so the connection will be closed when the response of HTTP is done. The TIME_WAIT
is the normal TCP state after closing a connection.
However, the default timeout of TIME_WAIT
state on Linux is 60 seconds. The huge number of TIME_WAIT
states could cause the server (such as probe/crawler) connection issue.
In order to solve the TIME_WAIT
issue. The SO_LINGER
option could help. It disables the default TCP delayed-close behavior, which sends the RST to the peer when the connection is closed. And it would remove the TIME_wAIT
state of the TCP connection.
More discussion could be found here When is TCP option SO_LINGER (0) required?
Sample
dialer := &net.Dialer{
Control: func(network, address string, conn syscall.RawConn) error {
var opterr error
if err := conn.Control(func(fd uintptr) {
l := &syscall.Linger{}
opterr = syscall.SetsockoptLinger(int(fd), unix.SOL_SOCKET, unix.SO_LINGER, l)
}); err != nil {
return err
}
return opterr
},
}
client := &http.Client{
Transport: &http.Transport{
DialContext: dialer.DialContext,
},
}
Moreover, here is another SO_LINGER
use case in EaseProbe. It is a simple, standalone, and lightweight tool that can do health/status checking.
Upvotes: 2