我正在尝试制作一个网络抓取工具,它可以每分钟运行相当数量(数千个)的HTTP查询。实际的查询很好,但是可以加快过程。我正在尝试使其并发。最初,我为每个请求生成了一个goroutine,但是我用尽了文件描述符,因此在进行一些谷歌搜索之后,我决定使用信号量来限制并发goroutine的数量。
只有我无法使它正常工作。
我尝试过移动一些代码,但是我总是遇到同样的问题:我运行的goroutine大约是我的三倍
这是我产生goroutines的唯一方法。我将goroutine限制为80个。在我的基准测试中,我对10000个URL进行了测试,它倾向于在飞行中同时运行约242个goroutine,但随后突然上升几乎翻了一番,然后又回落到242。 / p>
如果我将并发值从80更改为相同的行为,它通常徘徊在goroutine数量的三倍以上,有时会飙升到goroutine数量的两倍左右,我不知道为什么。
func (B BrandScraper) ScrapeUrls(URLs ...string) []scrapeResponse {
concurrent := 80
semaphoreChan := make(chan struct{}, concurrent)
scrapeResults := make([]scrapeResponse, len(URLs))
for _, URL := range URLs {
semaphoreChan <- struct{}{}
go func(URL string) {
defer func() {
<-semaphoreChan
}()
scrapeResults = append(scrapeResults,
B.getIndividualScrape(URL))
fmt.Printf("#goroutines: %d\n", runtime.NumGoroutine())
}(URL)
}
return scrapeResults
}
我希望它可以持续保持80个goroutine或至少恒定。
当我从基准测试中运行它或从主函数中运行它时,就会发生这种情况。
非常感谢您的提示!
编辑
getIndividualScrape
调用另一个函数:
func (B BrandScraper) doGetRequest(URL string) io.Reader {
resp, err := http.Get(URL)
if err != nil {
log.Fatal(err)
}
body, _ := ioutil.ReadAll(resp.Body)
resp.Body.Close()
return bytes.NewReader(body)
}
显然会发出HTTP请求。这可能是泄漏的goroutines吗?我以为自从我关闭resp.Body
以来,我已经解决了这个问题,但是也许没有?