【发布时间】:2019-01-31 16:23:06
【问题描述】:
我正在用 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.Transport 和 net.Dialer 运行请求没有任何区别,正如从 this playground 运行代码时可以看到的那样。
【问题讨论】:
-
您正在使用 http.DefaultClient 发出所有请求。当您通过几个独立的 http 客户端分发请求时会发生什么?可能连接池仅限于一定数量的连接。
-
重新编写了您的代码 (play.golang.org/p/HnKdFG5roj-),是的,我还发现一些结果相当可疑。不知道为什么它不能解决 web.mit.edu/fda.gov/geocities.jp/clickbank.net。但是恕我直言,它与并发率无关。
-
在路上也发现了这个,
2018/08/25 17:24:53 Unsolicited response received on idle HTTP channel starting with "HTTP/1.0 408 Request Time-out\r\nServer: AkamaiGHost\r\nMime-Version: 1.0\r\nDate: Sat, 25 Aug 2018 15:24:53 GMT\r\nContent-Type: text/html\r\nContent-Length: 218\r\nExpires: Sat, 25 Aug 2018 15:24:53 GMT\r\n\r\n<HTML><HEAD>\n<TITLE>Request Timeout</TITLE>\n</HEAD><BODY>\n<H1>Request Timeout</H1>\nThe server timed out while waiting for the browser's request.<P>\nReference&#32;&#35;2&#46;3ff90a17&#46;1535210693&#46;0\n</BODY></HTML>\n"; err=<nil> -
是的,和我之前的测试很相似,40次左右失败。还有一些我不安静,因为挖掘解决了它们。甚至
googleusercontent.com也经常失败。另见github.com/golang/go/issues/18588。我在 1.10 上运行它,我还没有花时间切换到 1.11,可能值得测试。 -
@Neverbolt 鉴于 GIL,python 代码很可能会变慢。很惊讶没有 DNS 工具来测量 QPS。在
gopacket中执行似乎很简单,但通过ebpf实现可能更有用。
标签: http go network-programming go-http