これは簡単な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接続でのみクラッシュします。
ありがとう、
最大ファイル記述子を変更する必要があると思います。私は以前に開発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
ソケットをオープン/リード/クローズする数百万の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()
}
Goのhttpパッケージは、デフォルトではリクエストのタイムアウトを指定しません。サービスには常にタイムアウトを含める必要があります。クライアントがセッションを閉じない場合はどうなりますか?プロセスは、ulimitに達する古いセッションを存続させます。悪役が意図的に何千ものセッションを開き、サーバーにDOSを実行する可能性があります。重負荷サービスも同様にulimitを調整する必要がありますが、バックストップのタイムアウトが必要です。
必ずタイムアウトを指定してください。
http.DefaultClient.Timeout = time.Minute * 10
プロセスによって開かれたファイルを監視することで、前後に検証できます。
lsof -p [PID_ID]
関数で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です
デフォルトでは、 "too many open files"エラーを回避するためにulimitを変更します。最大ulimitはlinuxで4096、macで1024です。 Linuxの/ securityフォルダーで、この行「* hard core 100000」を追加して、ハード制限を100000に設定します
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/
ファイル記述子の問題を回避するために、クローズ接続ヘッダーを手動で設定する必要もありました。
r, _ := http.NewRequest(http.MethodDelete, url, nil)
r.Close = true
res, err := c.Do(r)
res.Body.Close();
R.Close = trueおよびres.Body.Close()がなければ、ファイル記述子の制限に達しました。両方で、必要なだけ発射できました。