Протекающие горутины, как правило, имеют в три раза больше бега, чем я хочу - PullRequest
0 голосов
/ 19 января 2019

Я пытаюсь сделать веб-скребок, который может выполнять приличное количество (много тысяч) http-запросов в минуту. Реальный запрос это хорошо, но для ускорения процесса. Я пытаюсь сделать это одновременно. Первоначально я вызывал подпрограмму для каждого запроса, но у меня заканчивались файловые дескрипторы, поэтому после некоторого поиска в Google я решил использовать семафор, чтобы ограничить число одновременных подпрограмм.

Только я не могу заставить это работать.

Я пробовал перемещать фрагменты кода, но у меня всегда одна и та же проблема: у меня примерно в три раза больше выполняющихся программ, чем я хочу

Это единственный мой метод, который порождает горутины. Я ограничил количество подпрограмм до 80. В своих тестах я запускаю это для 10000 URL-адресов, и он имеет тенденцию зависать примерно при 242 одновременных прогонах в полете, но затем он внезапно увеличивается почти вдвое, а затем возвращается к 242.

Я получаю такое же поведение, если я изменяю параллельное значение с 80 - оно обычно превышает трехкратное количество горутин, а иногда всплывает примерно вдвое, и я понятия не имею, почему.

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 горутин - или, по крайней мере, постоянным.

Это происходит, когда я запускаю его из теста бенчмаркинга или когда я запускаю его из основной функции.

Большое спасибо за любые советы!

EDIT

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-запрос. Может ли это быть утечка горутин? Я думал, что с тех пор, как закрыл resp.Body, я бы покрыл это, но, возможно, нет?

...