Подтвердить что ты не робот

Go, tcp слишком много открытых файлов отладки

Вот простой тест на соединение HTTP (tcp) script

func main() {
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, client")
    }))
    defer ts.Close()
    var wg sync.WaitGroup
    for i := 0; i < 2000; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            resp, err := http.Get(ts.URL)
            if err != nil {
                panic(err)
            }
            greeting, err := ioutil.ReadAll(resp.Body)
            resp.Body.Close()
            if err != nil {
                panic(err)
            }
            fmt.Printf("%s", i, greeting)
        }(i)
    }
    wg.Wait()
}

И если я запустил это в Ubuntu, я получаю:

panic: Get http://127.0.0.1:33202: dial tcp 127.0.0.1:33202: too many open files

Другие сообщения говорят, что Close соединение, которое я делаю все это здесь. И другие говорят, что для увеличения предела максимального соединения с ulimit или попробуйте sudo sysctl -w fs.inotify.max_user_watches=100000, но все равно не работает.

Как запустить миллионы туннелей gpoutines на одном сервере? Он разбивается только на 2000 соединений.

Спасибо,

4b9b3361

Ответ 1

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

FWIW, ваша программа отлично работает на моей виртуальной машине.

·> ulimit -n
120000

Но после того, как я бегу

·> ulimit -n 500
·> ulimit -n
500

я получаю:

panic: Get http://127.0.0.1:51227: dial tcp 127.0.0.1:51227: socket: too many open files

** Не попадайся в ловушку, которую устроила Правин **

Примечание ulimit! = ulimit -n.

➜  cmd git:(wip-poop) ✗ 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 (kbytes)          unlimited
-l: locked-in-memory size (kbytes)  unlimited
-u: processes                       1418
-n: file descriptors                4864

Ответ 2

Если вы хотите запускать миллионы ходовых процедур, которые открывают/читают/закрывают сокет, хорошо вы улучшаете свой ulimit или открываете/читаете/закрываете сокет и передаете значение, прочитанное в go-daily, но я будет использовать буферный канал для управления количеством файловых дескрипторов, которые вы хотите открыть.

const (
    // this is where you can specify how many maxFileDescriptors
    // you want to allow open
    maxFileDescriptors = 100
)

func main() {
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, client")
    }))
    defer ts.Close()
    var wg sync.WaitGroup
    maxChan := make(chan bool, maxFileDescriptors)
    for i := 0; i < 1000; i++ {
        maxChan <- true
        go func(url string, i int, maxChan chan bool, wg *sync.WaitGroup) {
            wg.Add(1)
            defer wg.Done()
            defer func(maxChan chan bool) { <-maxChan }(maxChan)
            resp, err := http.Get(url)
            if err != nil {
                panic(err)
            }
            greeting, err := ioutil.ReadAll(resp.Body)
            if err != nil {
                panic(err)
            }
            err = resp.Body.Close()
            if err != nil {
                panic(err)
            }
            fmt.Printf("%d: %s", i, string(greeting))
        }(ts.URL, i, maxChan, &wg)
    }
    wg.Wait()
}

Ответ 3

Gos http пакет не определяет время ожидания запроса по умолчанию. Вы всегда должны включать тайм-аут в вашем сервисе. Что делать, если клиент не закрывает свою сессию? Ваш процесс будет поддерживать старые сессии, достигая предельных значений. Плохой актер может преднамеренно открыть тысячи сессий, используя ваш сервер. Сервисы с высокой нагрузкой также должны корректировать предельные значения, но время ожидания для обратного останова.

Убедитесь, что вы указали тайм-аут:

http.DefaultClient.Timeout = time.Minute * 10

Вы можете проверить до и после, наблюдая за файлами, открытыми вашим процессом:

lsof -p [PID_ID]

Ответ 4

тоже может goruntine в вашей функции, попробуйте https://github.com/leenanxi/nasync

 //it has a simple usage
 nasync.Do(yourAsyncTask)

в вашем коде

for i := 0; i < 2000; i++ {
    nasync.Do(func() {
        resp, err := http.Get("https://www.baidu.com")
        ...
    })
}

по умолчанию max go goruntine в nasync lib 1000

Ответ 5

измените ulimit, чтобы избежать ошибки "слишком много открытых файлов" по умолчанию max ulimit - 4096 для linux и 1024 для mac, u может изменить ulimit до 4096, набрав ulimit -n 4096 за пределами 4096 вам необходимо изменить limit.conf в папке etc/security для linux и установить жесткий предел до 100000, добавив эту строку "* hard core 100000"

Ответ 6

HTTP/1.1 uses persistent connections by default:
A significant difference between HTTP/1.1 and earlier versions of HTTP is that persistent connections are the default behavior of any HTTP connection.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html
The solution was to inform the server that the client wants to close the connection after the transaction is complete. This can be done by setting the Connection header,
req.Header.Set("Connection", "close") or by setting the Close property to true on the http.Request:
req.Close = true After doing that, the "too many open files" issue went away as the program was no longer keeping HTTP connections open and thus not using up file descriptors.

Я решил это, добавив req.Close = true и req.Header.Set( "Соединение", "закрыть" ). Я думаю, это лучше, чем изменение ulimit.

источник: http://craigwickesser.com/2015/01/golang-http-to-many-open-files/

Ответ 7

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

r, _ := http.NewRequest(http.MethodDelete, url, nil)
r.Close = true
res, err := c.Do(r)
res.Body.Close();

Без r.Close = true и res.Body.Close() Я попал в лимит дескриптора файла. С обоими я мог бы выстрелить столько, сколько мне было нужно.