Я пытаюсь сделать веб-скребок, который может выполнять приличное количество (много тысяч) 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
, я бы покрыл это, но, возможно, нет?