Поиск [HOST]: нет такой ошибки хоста в Go

У меня есть эта тестовая программа, которая будет извлекать url параллельно, но когда я увеличиваю параллельное число до 1040, я начинаю получать ошибку lookup www.httpbin.org: no such host.

После некоторых Google я нашел, что другие говорят, что не закрыть ответ, вызовет проблему, но я закрываю это с помощью res.Body.Close().

В чем проблема? Большое спасибо.

package main

import (
    "fmt"
    "net/http"
    "io/ioutil"
)

func get(url string) ([]byte, error) {

    client := &http.Client{}
    req, _ := http.NewRequest("GET", url, nil)

    res, err := client.Do(req)

    if err != nil {
        fmt.Println(err)
        return nil, err
    } 

    bytes, read_err := ioutil.ReadAll(res.Body)
    res.Body.Close()

    fmt.Println(bytes)

    return bytes, read_err
}

func main() {
    for i := 0; i < 1040; i++ {
        go get(fmt.Sprintf("http://www.httpbin.org/get?a=%d", i))
    }
}

Ответ 1

Это потому, что у вас может быть до 1040 одновременных вызовов в вашем коде, поэтому вы вполне можете быть в состоянии с открытым диском 1040 и еще не закрыты.

Вам нужно ограничить количество используемых goroutines.

Здесь одно возможное решение с ограничением до 100 одновременных вызовов max:

func getThemAll() {
    nbConcurrentGet := 100
    urls :=  make(chan string, nbConcurrentGet)
    for i := 0; i < nbConcurrentGet; i++ {
        go func (){
            for url := range urls {
                get(url)
            }
        }()
    }
    for i:=0; i<1040; i++ {
        urls <- fmt.Sprintf("http://www.httpbin.org/get?a=%d", i)
    }
}

Если вы вызываете это в основной функции вашей программы, он может остановиться, прежде чем все задачи будут завершены. Вы можете использовать sync.WaitGroup, чтобы предотвратить это:

func main() {
    nbConcurrentGet := 100
    urls :=  make(chan string, nbConcurrentGet)
    var wg sync.WaitGroup
    for i := 0; i < nbConcurrentGet; i++ {
        go func (){
            for url := range urls {
                get(url)
                wg.Done()
            }
        }()
    }
    for i:=0; i<1040; i++ {
        wg.Add(1)
        urls <- fmt.Sprintf("http://www.httpbin.org/get?a=%d", i)
    }
    wg.Wait()
    fmt.Println("Finished")
}

Ответ 2

а технически ваш процесс ограничен (ядром) примерно до 1000 открытых файловых дескрипторов. В зависимости от контекста вам может потребоваться увеличить это число.

В прогоне оболочки (обратите внимание на последнюю строку):

$ ulimit -a
-t: cpu time (seconds)         unlimited
-f: file size (blocks)         unlimited
-d: data seg size (kbytes)     unlimited
-s: stack size (kbytes)        8192
-c: core file size (blocks)    0
-v: address space (kb)         unlimited
-l: locked-in-memory size (kb) unlimited
-u: processes                  709
-n: file descriptors           2560

Увеличить (временно):

$ ulimit -n 5000
(no output)

Затем проверьте предел fd:

$ ulimit -n
5000