私が書いている HTTP サーバーを高負荷時に適切に動作させようとしていますが、よく理解できない奇妙な動作が発生しています。
私のテストでは、サーバー プロセスを追跡しながらab
、ループバック インターフェース上で 1000 の同時実行レベル ( ) で (Apache ベンチマーク プログラム) を使用します。ab -n 50000 -c 1000 http://localhost:8080/apa
Strace は、問題がすぐに再現できるように処理を十分に遅くし、完了後にサーバーの内部をある程度デバッグできるようにします。tcpdump
また、テストの実行中にネットワーク トラフィックをキャプチャします。
何が起こるかというとab
、テストの実行中にしばらく停止し、接続が返されたと不平を言うことECONNRESET
です。これは少し奇妙に感じます。サーバーにはすべてを処理するための帯域幅がない可能性があるため、接続のタイムアウトを簡単に受け入れることができますが、それは合理的に戻るべきではないETIMEDOUT
か、またはECONNREFUSED
すべての接続を受け入れることができない場合でも?
Wireshark を使用して、返される最初の接続を構成するパケットを抽出しましたECONNRESET
。その簡単なパケット リストは次のように
なります。 (この接続のファイル
全体は、ここで入手できます。)
tcpdump
このダンプからわかるように、(SYN
数回の再送信の後) 接続が受け入れられ、その後、要求が数回再送信され、サーバーが接続をリセットします。私は疑問に思っています、これが起こる原因は何ですか?通常、Linux の TCP 実装は、TCP ウィンドウにスペースがある限り、読み取りプロセスが受信を選択する前にデータに ACK を送信します。不足しているある種の共有バッファはありますか? 最も重要なことは、カーネルRST
が単に待機してクライアントにさらに再送信させるのではなく、突然パケットで応答するのはなぜですか?
記録として、プロセスの strace は、この接続のポート (ポート 56946) からの接続を受け入れることさえないことを示しているため、これは Linux が独自に行うことのようです。ab の同時実行レベルが十分に低い限り、サーバーは完全に正常に動作し (約 100 までは完全に正常に動作し、その後 100 から 500 の間のどこかで断続的に失敗し始めます)、その要求スループットがかなり一定であることも注目に値します。同時実行レベルに関係なく (トレースされていない限り、1 秒あたり 6000 ~ 7000 のリクエストを処理します)。問題が発生する頻度とバックログの設定との間に特定の相関関係は見つかりませんでしたlisten()
(現在は 128 を使用していますが、1024 まで試してみましたが、違いはないようです)。
念のために言うと、私はこの AMD64 ボックスで Linux 3.2.0 を実行しています。