同时请求拨打TCP I / O超时

时间:2018-08-25 12:03:59

标签: http go network-programming go-http

我正在Go中构建一个工具,该工具需要同时向许多不同的服务器发出大量的HTTP请求。我最初使用Python编写的原型完全可以同时执行数百个请求。

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

我已经在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()
}

正在发生两个特定的错误消息,一个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.Transportnet.Dialer运行请求并没有任何区别,就像从this playground运行代码时看到的那样。

1 个答案:

答案 0 :(得分:0)

我认为您的许多 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"?How to set ulimit -n from a golang program? 找到有关此特定问题的更多信息。 (在我的机器 (macOS) 上,手动增加文件限制似乎有帮助,但我认为这不是一个好的解决方案,因为它并没有真正扩展,而且我不确定您总共需要多少打开的文件。 )


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