web-dev-qa-db-ja.com

Go、tcpが開いているファイルが多すぎるデバッグ

これは簡単なGo http(tcp)接続テストスクリプトです

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しかし、まだ機能しません。

単一のサーバーで何百万ものtcp接続ゴルーチンを実行するにはどうすればよいですか? 2,000接続でのみクラッシュします。

ありがとう、

13
user4211028

最大ファイル記述子を変更する必要があると思います。私は以前に開発VMの1つで同じ問題に遭遇しましたが、ファイル記述子の最大値を変更する必要がありました。

FWIW、あなたのプログラムは私のVMで問題なく動作します。

·> 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

** Praveenがやったtrapに陥らないでください**

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
28
sberry

ソケットをオープン/リード/クローズする数百万のgoルーチンを実行したい場合は、ulimitを増やすか、ソケットをオープン/リード/クローズして、値をgo-routineに渡しますが、私はバッファリングされたチャネルを使用して、開くことができるファイル記述子の数を制御します。

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()
}
5
Popmedic

Goのhttpパッケージは、デフォルトではリクエストのタイムアウトを指定しません。サービスには常にタイムアウトを含める必要があります。クライアントがセッションを閉じない場合はどうなりますか?プロセスは、ulimitに達する古いセッションを存続させます。悪役が意図的に何千ものセッションを開き、サーバーにDOSを実行する可能性があります。重負荷サービスも同様にulimitを調整する必要がありますが、バックストップのタイムアウトが必要です。

必ずタイムアウトを指定してください。

http.DefaultClient.Timeout = time.Minute * 10

プロセスによって開かれたファイルを監視することで、前後に検証できます。

lsof -p [PID_ID]
2
PodTech.io

関数で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")
        ...
    })
}

nasync libのデフォルトの最大go goruntineは1000です

1
lee

デフォルトでは、 "too many open files"エラーを回避するためにulimitを変更します。最大ulimitはlinuxで4096、macで1024です。 Linuxの/ securityフォルダーで、この行「* hard core 100000」を追加して、ハード制限を100000に設定します

0
sp111
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( "Connection"、 "close")を追加してこれを解決しました。 ulimitを変更するよりも良いと思います。

ソース: http://craigwickesser.com/2015/01/golang-http-to-many-open-files/

0
余云鹏

ファイル記述子の問題を回避するために、クローズ接続ヘッダーを手動で設定する必要もありました。

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

R.Close = trueおよびres.Body.Close()がなければ、ファイル記述子の制限に達しました。両方で、必要なだけ発射できました。

0
Matt Borowiec