在同时请求时拨打 tcp I/O 超时

Nev*_*olt 9 network-programming http go go-http

我正在用 Go 构建一个工具,它需要向许多不同的服务器发出大量并发 HTTP 请求。我在 Python 中的初始原型在处理几百个并发请求时没有问题。

但是,我发现在 Go 中Get http://www.google.com: dial tcp 216.58.205.228:80: i/o timeout,如果同时请求的数量超过 ~30-40,这几乎总是会导致某些情况。

我已经在 macOS、openSUSE、不同的硬件、不同的网络和不同的域列表上进行了测试,并且按照其他 Stackoverflow 答案中的描述更改 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()
}
Run Code Online (Sandbox Code Playgroud)

发生了两个特定的错误消息,一个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{}
Run Code Online (Sandbox Code Playgroud)

更新:

运行带有一个单独的请求http.Client,以及http.Transportnet.Dialer没有任何差别,如从运行的代码时,可以看到这个游乐场

Cam*_*tle 1

我认为你的许多net.DNSError错误实际上是too many open files伪装的错误。您可以通过使用标签netgo来自此处的推荐)(go run -tags netgo main.go)运行示例代码来查看这一点,这将发出如下错误:

\n
\xe2\x80\xa6dial tcp: lookup buzzfeed.com on 192.168.1.1:53: dial udp 192.168.1.1:53: socket: too many open files\n
Run Code Online (Sandbox Code Playgroud)\n

代替

\n
\xe2\x80\xa6dial tcp: lookup buzzfeed.com: no such host\n
Run Code Online (Sandbox Code Playgroud)\n

确保您正在关闭请求的响应正文 ( resp.Body.Close())。您可以在处理“打开文件过多”的最佳方法是什么?中找到有关此特定问题的更多信息。以及如何从 golang 程序设置 ulimit -n?。(在我的机器(macOS)上,手动增加文件限制似乎有帮助,但我不认为这是一个好的解决方案,因为它不能真正扩展,而且我不确定您打开了多少个文件\需要整体。)

\n
\n

正如 @liam-kelly 所建议的,我认为i/o timeout错误来自 DNS 服务器或其他一些安全机制。设置自定义(错误)DNS 服务器 IP 也会出现同样的错误。

\n