拨号 tcp I/O 并发请求超时
Dial tcp I/O timeout on simultaneous requests
我正在用 Go 构建一个工具,它需要向许多不同的服务器同时发出大量的 HTTP 请求。我在 Python 中的初始原型处理几百个并发请求没有问题。
但是,我发现在 Go 中,如果同时请求的数量超过 ~30-40,这几乎总是会导致 Get http://www.google.com: dial tcp 216.58.205.228:80: i/o timeout
。
我已经在 macOS、openSUSE、不同的硬件、不同的网络和不同的域列表上进行了测试,并且按照其他 Whosebug 答案中的描述更改 DNS 服务器也不起作用。
有趣的是,失败的请求甚至不会产生数据包,这在使用 Wireshark 进行检查时可以看出。
我有什么地方做错了还是 Go 中的错误?
可重现的最小程序如下:
package main
import (
"fmt"
"net/http"
"sync"
)
func main() {
domains := []string{/* large domain list here, eg from https://moz.com/top500 */}
limiter := make(chan string, 50) // Limits simultaneous requests
wg := sync.WaitGroup{} // Needed to not prematurely exit before all requests have been finished
for i, domain := range domains {
wg.Add(1)
limiter <- domain
go func(i int, domain string) {
defer func() { <-limiter }()
defer wg.Done()
resp, err := http.Get("http://"+domain)
if err != nil {
fmt.Printf("%d %s failed: %s\n", i, domain, err)
return
}
fmt.Printf("%d %s: %s\n", i, domain, resp.Status)
}(i, domain)
}
wg.Wait()
}
出现了两个特定的错误消息,一个没有任何意义的 net.DNSError
和一个没有描述的 poll.TimeoutError
:
&url.Error{Op:"Get", URL:"http://harvard.edu", Err:(*net.OpError)(0xc00022a460)}
&net.OpError{Op:"dial", Net:"tcp", Source:net.Addr(nil), Addr:net.Addr(nil), Err:(*net.DNSError)(0xc000aca200)}
&net.DNSError{Err:"no such host", Name:"harvard.edu", Server:"", IsTimeout:false, IsTemporary:false}
&url.Error{Op:"Get", URL:"http://latimes.com", Err:(*net.OpError)(0xc000d92730)}
&net.OpError{Op:"dial", Net:"tcp", Source:net.Addr(nil), Addr:net.Addr(nil), Err:(*poll.TimeoutError)(0x14779a0)}
&poll.TimeoutError{}
更新:
运行 带有单独 http.Client
以及 http.Transport
和 net.Dialer
的请求没有任何区别,正如 运行 代码中所见来自 this playground.
我认为您的许多 net.DNSError
实际上是伪装的 too many open files
错误。您可以通过带有 netgo
标记 (recommendation from here) (go run -tags netgo main.go
) 的 运行 示例代码看到这一点,它会发出如下错误:
…dial tcp: lookup buzzfeed.com on 192.168.1.1:53: dial udp 192.168.1.1:53: socket: too many open files
而不是
…dial tcp: lookup buzzfeed.com: no such host
确保您正在关闭请求的响应正文 (resp.Body.Close()
)。您可以在 What's the best way to handle "too many open files"? and How to set ulimit -n from a golang program? 找到有关此特定问题的更多信息。 (在我的机器 (macOS) 上,手动增加文件限制似乎有所帮助,但我认为这不是一个好的解决方案,因为它并没有真正扩展,而且我不确定您总共需要多少个打开的文件。 )
正如@liam-kelly 所建议的,我认为 i/o timeout
错误来自 DNS 服务器或其他一些安全机制。设置自定义(错误的)DNS 服务器 IP 会给我同样的错误。
我正在用 Go 构建一个工具,它需要向许多不同的服务器同时发出大量的 HTTP 请求。我在 Python 中的初始原型处理几百个并发请求没有问题。
但是,我发现在 Go 中,如果同时请求的数量超过 ~30-40,这几乎总是会导致 Get http://www.google.com: dial tcp 216.58.205.228:80: i/o timeout
。
我已经在 macOS、openSUSE、不同的硬件、不同的网络和不同的域列表上进行了测试,并且按照其他 Whosebug 答案中的描述更改 DNS 服务器也不起作用。
有趣的是,失败的请求甚至不会产生数据包,这在使用 Wireshark 进行检查时可以看出。
我有什么地方做错了还是 Go 中的错误?
可重现的最小程序如下:
package main
import (
"fmt"
"net/http"
"sync"
)
func main() {
domains := []string{/* large domain list here, eg from https://moz.com/top500 */}
limiter := make(chan string, 50) // Limits simultaneous requests
wg := sync.WaitGroup{} // Needed to not prematurely exit before all requests have been finished
for i, domain := range domains {
wg.Add(1)
limiter <- domain
go func(i int, domain string) {
defer func() { <-limiter }()
defer wg.Done()
resp, err := http.Get("http://"+domain)
if err != nil {
fmt.Printf("%d %s failed: %s\n", i, domain, err)
return
}
fmt.Printf("%d %s: %s\n", i, domain, resp.Status)
}(i, domain)
}
wg.Wait()
}
出现了两个特定的错误消息,一个没有任何意义的 net.DNSError
和一个没有描述的 poll.TimeoutError
:
&url.Error{Op:"Get", URL:"http://harvard.edu", Err:(*net.OpError)(0xc00022a460)}
&net.OpError{Op:"dial", Net:"tcp", Source:net.Addr(nil), Addr:net.Addr(nil), Err:(*net.DNSError)(0xc000aca200)}
&net.DNSError{Err:"no such host", Name:"harvard.edu", Server:"", IsTimeout:false, IsTemporary:false}
&url.Error{Op:"Get", URL:"http://latimes.com", Err:(*net.OpError)(0xc000d92730)}
&net.OpError{Op:"dial", Net:"tcp", Source:net.Addr(nil), Addr:net.Addr(nil), Err:(*poll.TimeoutError)(0x14779a0)}
&poll.TimeoutError{}
更新:
运行 带有单独 http.Client
以及 http.Transport
和 net.Dialer
的请求没有任何区别,正如 运行 代码中所见来自 this playground.
我认为您的许多 net.DNSError
实际上是伪装的 too many open files
错误。您可以通过带有 netgo
标记 (recommendation from here) (go run -tags netgo main.go
) 的 运行 示例代码看到这一点,它会发出如下错误:
…dial tcp: lookup buzzfeed.com on 192.168.1.1:53: dial udp 192.168.1.1:53: socket: too many open files
而不是
…dial tcp: lookup buzzfeed.com: no such host
确保您正在关闭请求的响应正文 (resp.Body.Close()
)。您可以在 What's the best way to handle "too many open files"? and How to set ulimit -n from a golang program? 找到有关此特定问题的更多信息。 (在我的机器 (macOS) 上,手动增加文件限制似乎有所帮助,但我认为这不是一个好的解决方案,因为它并没有真正扩展,而且我不确定您总共需要多少个打开的文件。 )
正如@liam-kelly 所建议的,我认为 i/o timeout
错误来自 DNS 服务器或其他一些安全机制。设置自定义(错误的)DNS 服务器 IP 会给我同样的错误。