TCP 快速開啟
TCP 快速開啟(簡稱 TFO)是 TCP 協定的延伸功能,允許在建立 TCP 連線時,與初始 SYN 封包一同傳送少量資料。在某些情況下,這可以節省往返路徑和減少回應時間。
但並非總是可以使用 TFO,因為它會改變 TCP 的行為:接收端可能會因為 SYN 封包的重傳而看到這些資料遭重複傳送。因此,只有在初始封包中的資料能夠以「冪等」方式處理時,才應該使用 TFO。資料是否能夠這樣處理,則取決於協定和應用程式。
保證冪等性的其中一個重要用途,就是 TLS 的 Client Hello 訊息。這是客戶端發送給伺服器的第一個訊息,用於啟動 TLS 握手。這在建立 TLS 連線時可以節省一個來回行程。從 4.1.61.Final
版本開始,Netty 中的 SslHandler
在可用時會自動利用 TFO 的優勢。特別是,epoll 傳輸僅支援伺服器端和客戶端 TFO,並且需要 Linux kernel 中啟用支援。從 4.1.67.Final
版本開始,TCP FastOpen 也將支援 kqueue
傳輸上的客戶端連線。
首先,作業系統中必須支援並啟用 TFO。在 MacOS 上,這是預設啟用的。在 Linux 上,這由 /proc/sys/net/ipv4/tcp_fastopen
檔案控制。檔案可能包含下列其中一個值
- TFO 未啟用。
- 為傳送連線(客戶端)啟用 TFO。
- 為接收連線(伺服器)啟用 TFO。
- 為客戶端和伺服器都啟用 TFO。
設定可以藉由以 root
使用者將衍生的組態值寫入檔案來變更。
第二個步驟是在 Netty 中啟用 TFO。這對於伺服器和客戶端來說,以不同的方式來進行
對於伺服器,您將 ChannelOption.TCP_FASTOPEN
設定為 ServerBootstrap
的選項
ServerBootstrap sb = ...;
sb.option(ChannelOption.TCP_FASTOPEN, maxPendingFastOpen);
這就這樣而已。該選項指定在任何時間點,Socket 上可以保留多少個快速開啟的要求。這會限制可以綁定於尚未建立連線的快速開啟酬載的系統資源。請參閱 RFC 7413 被動開啟附錄 作為參考。
對於客戶端,情況稍微複雜一點。您首先將 ChannelOption.TCP_FASTOPEN_CONNECT
選項設定為 Bootstrap
上的 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()
方法,因為那會建立另一個自己的傳送緩衝區的新通道。