泄漏的goroutine通常运行的次数是我想要的三倍

时间:2019-01-18 21:49:04

标签: go concurrency goroutine

我正在尝试制作一个网络抓取工具,它可以每分钟运行相当数量(数千个)的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以来,我已经解决了这个问题,但是也许没有?

0 个答案:

没有答案