HTTPのためのTCPチューニング (Best Current Practice)

HTTPを効率よく使用するためにTCPのチューニングはかかせません。


Best Current Practiceカテゴリとして「TCP Tuning for HTTP (draft 00)」というInternet-Draftが提出されているので、ざっと目を通した。(割と適当)


大きく分けて下記の5つについて書かれており、特筆のない限りHTTP/1.1でもHTTP/2有効なプラクティスです。

  • 2 Socket planning
  • 3 TCP handshake
  • 4 TCP transfers
  • 5 Re-using connections
  • 6 Closing connections

(1はイントロダクション

2 Socket planning

2.1 Number of open files

殆どのOSでソケットを開ける数は、オープンできるファイル数と等しいです。
その上限がボトルネックになっていないことを確認する。


Linuxでは上限を上げることが出来ます

   fs.file-max = <number of files>
2.2. Number of concurrent network messages

カーネルが処理できる以上のパケットを受信した際にキューに積むことが出来るパケットの数を上げる

Linuxでは以下のとおり指定できる

  net.core.netdev_max_backlog = <number of packets>
2.3. Number of incoming TCP SYNs allowed to backlog

新しいコネクション要求をキューに積む事ができる数。

Linuxでは以下のとおり指定できる

   net.core.somaxconn = <number>
2.4. Use the whole port range for local ports

TCPスタックが最大限ソケットを全て使用できるように、ローカルポートの数を広げる。

  net.ipv4.ip_local_port_range = 1024 65535


2.5. Lower the TCP FIN timeout
コネクションがFIN-WAIT-2状態の時間を少なくすることで、より早く再利用できるようになり同時接続数を増やすことが出来ます。

   net.ipv4.tcp_fin_timeout = <number of seconds>
2.6. Re-use sockets in TIME_WAIT state

ネットワークスタックの観点から安全である場合は特に、新しい接続のためのTIME_WAIT状態にあるソケットの再利用を可能にします。

   net.ipv4.tcp_tw_reuse = 1
2.7. Give the the TCP stack enough memory

高速に大量のTCPコネクションを処理するシステムはTCPスタックバッファに沢山のメモリを必要とします。
TCPスタックにデフォルトのバッファサイズや、どれくらい動的に拡大・縮小していいか指定できます。

   net.ipv4.tcp_wmem = <minimum size> <default size> <max size in bytes>
   net.ipv4.tcp_rmem = <minimum size> <default size> <max size in bytes>
2.8. Set maximum allowed TCP window sizes

許可された最大ウィンドウサイズを増やしたほうが良いかもしれない。

   net.core.rmem_max = <number of bytes>
   net.core.wmem_max = <number of bytes>
2.9. Timers and time-outs

失敗を早くする。とても長いタイムアウトを許可しない。
さまざまなネットワーク関連の試みに数分を無駄にしても、ユーザーが幸せになるわけではありません。
(見た目上)何もしていないTCPフローのアイドル状態を避ける。 その代わりに、HTTPでの202やリダイレクトの処理をする。

3. TCP handshake

3.1. TCP Fast Open

TCP Fast OpenはTCPハンドシェイク中にデータ送信を許可します。それによって、コネクションがオープンでない場合でも遅延なしにリクエストを送信することが出来ます。


TFOはクライアントとサーバ両方のサポートが必要です。SYNで送られるデータが冪等である必要が有るため、アプリケーション側もその事に対する認識が必要です。


それゆえ、TFOは冪等なときのみ使用でき、GETやHEADと言ったHTTPメソッドや、TLSなどを使用している時に使用することが出来ます。


TFOのサポートが原因でそれが与える重要なパフォーマンス上の利点に、モバイル環境などがあります。

3.2. Initial Congestion Window

RFC6928では初期服装ウィンドウを10としていますし、サーバサイドでは広くデプロイされています。


パケットペーシングとの組み合わせで、より大きな初期ウィンドウを用いた実験がありました。IW10があっても、大量のサーバに非常にフェアに実行されることが報告されています。

3.3. TCP SYN flood handling

TCP SYN Floodの緩和(RFC4987)は必要であり、閾値をいじれます。

4. TCP transfers

4.1. Packet Pacing

TBD

4.2. Explicit Congestion Control

iOSOSXで使用できる
https://developer.apple.com/videos/wwdc/2015/?id=719

4.3. Nagle's Algorithm

Nagle'sアルゴリズムは(RFC0896)は、送信するパケットを少しだけとどめて次に送信するパケットとマージする仕組みです。遅延を犠牲にスループットを最適化します。


特にHTTP/2では、片方向に受信が行われている間は高速にパケットを返すことが要求されます。小さな遅延でも重大なパフォーマンス損失を引き起こします。


HTTP/1.1でも、一つの write()システムコールでリクエスト全てを送信するときに特に影響を受けます。
POSIXシステムでは下記のように変更できます。

   int one = 1;
   setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
4.4. Keep-alive

TCPキープアライブはモバイルでは少なくとも省エネルギーのために無効になっています。
アプリケーションレベルのキープアライブは、生存期間の長い接続のために通信相手やステートフルなファイアウォールによってコネクションをリセットされたことを検知する必要があります。

5. Re-using connections

5.1. Slow Start after Idle

スロースタートはネットワークの中でTCPが使用する輻輳制御アルゴリズムの一つです。
それは、指数増加期間としても知られています。各TCPコネクションはslow-startで開始されますが、ある一定時間のアイドル後にまたslow-startに戻ります。


Linuxシステムでは、設定によってによってアイドル後にスロースタートに戻ることを防ぐことが出来ます。

   net.ipv4.tcp_slow_start_after_idle = 0
5.2. TCP-Bound Authentications

一つのHTTPリクエストを、ではなくはコネクションを認証するのに幾つかのHTTP認証メカニズムが今日使用されています。主に使用されているものにNTLM とNegotiateがあります。

もし、これらの認証はTCPコネクション上でネゴシエーションされるのであれば、その接続は、その生存期間の間認証されたままにすることができます。他のHTTP認証の仕組みとの食い違いは、非常に重要で注意して処理する必要があります。

6. Closing connections

6.1. Half-close

クライアントやサーバはリクエストやレスポンスの後にハーフクローズに出来ます。HTTP/2では停止中のストリームが無ければ。

ハーフクローズは稀にサーバが綺麗にコネクションを切断する唯一の方法となります。それは、サーバはさらなるリクエストの受け入れをせず、クライアントに送信中のレスポンスの受信を許可する。

6.2. Abort

クライアントはリクエストボディを送信し終わった後にHTTP/1.1の接続をabortしません。
RSTを防ぐためにフルクローズにはエラーレスポンスが期待されます。

6.3. Close Idle Connections

後続の接続のために、接続を再利用できるように維持することは、HTTPクライアントのパフォーマンスにとって重要です。
既存のコネクションの価値はすぐに低下し、数分後にはブラウザによって再利用される可能性はわずかです。