TCP Fast Open
TCP Fast Open(TFO)は、TCP接続の確立時に、初期SYNパケットと一緒に少量のデータを送信できるようにするTCPプロトコルの拡張機能です。 これにより、特定のケースでラウンドトリップを節約し、応答時間を短縮できます。
ただし、TFOはTCPの動作を変更するため、常に使用できるとは限りません。受信側は、SYNパケットの再送信により、このデータが重複して表示される場合があります。 このため、TFOは、初期パケットのデータが冪等的に処理される場合にのみ使用してください。 これが当てはまるかどうかは、プロトコルとアプリケーションによって異なります。
冪等性が保証される重要なユースケースの1つは、TLS Client Helloメッセージです。 これは、クライアントがTLSハンドシェイクを開始するためにサーバーに送信する最初のメッセージです。 これにより、TLS接続を確立する際のラウンドトリップが節約されます。 NettyのSslHandler
は、バージョン4.1.61.Final
以降、利用可能な場合はTFOを自動的に活用します。 具体的には、サーバー側とクライアント側のTFOはepollトランスポートでのみサポートされており、Linuxカーネルでサポートが有効になっている必要があります。バージョン4.1.67.Final
以降、TCP FastOpenはkqueue
トランスポートでのクライアント側接続でもサポートされます。
まず、TFOがオペレーティングシステムでサポートされ、有効になっている必要があります。 *MacOS*では、これはデフォルトで有効になっています。 *Linux*では、これは/proc/sys/net/ipv4/tcp_fastopen
ファイルで制御されます。 このファイルには、次のいずれかの値が含まれている場合があります。
- TFOは無効です。
- TFOは発信接続(クライアント)に対して有効です。
- TFOは着信接続(サーバー)に対して有効です。
- TFOはクライアントとサーバーの両方で有効です。
設定は、ルートユーザーとして目的の構成値をファイルに書き込むことで変更できます。
2番目の手順は、NettyでTFOを有効にすることです。 これは、サーバーとクライアントで異なる方法で行われます。
サーバーの場合は、ServerBootstrap
でオプションとしてChannelOption.TCP_FASTOPEN
を設定します。
ServerBootstrap sb = ...;
sb.option(ChannelOption.TCP_FASTOPEN, maxPendingFastOpen);
これで完了です。 このオプションは、ソケットでいつでも保留できるfast-openリクエストの数を指定します。 これにより、確立されていない接続のfast-openペイロードに拘束される可能性のあるシステムリソースが制限されます。 詳細については、RFC 7413のパッシブオープンに関する付録を参照してください。
クライアントの場合は、少し複雑です。 まず、Bootstrap
でChannelOption.TCP_FASTOPEN_CONNECT
オプションをtrue
に設定します。
Bootstrap cb = ...;
cb.option(ChannelOption.TCP_FASTOPEN_CONNECT, true);
次に、再送信によって複数回到着する可能性があることを考慮して、SYNパケットと一緒に送信できるデータを決定する必要があります。 このデータは、チャネルが接続される*前*に、チャネルの送信バッファに配置する必要があります。 接続される前のチャネルを取得するには、register
を呼び出します。 この例を次に示します。
Bootstrap cb = new Bootstrap();
cb.option(ChannelOption.TCP_FASTOPEN_CONNECT, true);
// ...set handler, etc...
Channel channel = cb.register().sync().channel(); // Get unconnected channel.
ByteBuf fastOpenData = ...;
ByteBuf normalData = ...;
channel.write(fastOpenData); // Write TFO data.
channel.connect(remoteAddress).sync(); // Establish connection (flushes TFO data).
channel.write(normalData); // TCP connection works like normal now.
上記に関する重要な点:register()
によって生成されたチャネルでconnect()
を呼び出します。 Bootstrap.connect()
メソッドは使用できません。これは、独自の送信バッファを持つ別の新しいチャネルを作成するためです。