接続の「再利用」という哲学:HTTP/1.1 `Keep-Alive` が変えたネットワークの深層
ネットワークエンジニアの端くれとして、TCPの3ウェイ・ハンドシェイクのログを見つめる瞬間ほど、空虚なものはない。SYN、SYN-ACK、ACK。この「儀式」に費やされるミリ秒は、現代のWebアプリケーションにおいて、まさに「死」に等しい。
HTTP/1.0の時代、ブラウザはリクエストのたびに新しいTCP接続を張っていた。パケットが光速で移動しても、OSのスタックで費やされるオーバーヘッド、そして何よりTLSハンドシェイクの暗号学的計算コストは、ユーザー体験を容赦なく削り取っていた。そこで登場したのが、HTTP/1.1における `Connection: keep-alive` という、いわば「接続の永続化」という哲学だ。
1. なぜ「ハンドシェイク」を殺すべきなのか
TCP接続を確立する際、クライアントとサーバー間では必ずRTT(Round Trip Time)が少なくとも1.5〜2往復発生する。TLS 1.2以前であれば、さらに数往復の暗号ネゴシエーションが加わる。
もし、Webページが100個の静的リソース(画像、CSS、JS)を読み込むとして、その都度接続を閉じていたらどうなるか? 想像するだけで冷や汗が出る。`Keep-Alive` は、この「つなぎっぱなし」を実現することで、後続のリクエストを既存の確立済みパイプラインに流し込む。これにより、TCPのSlow Start問題(輻輳ウィンドウの拡大)による初期低速化を一度の接続で済ませ、パケットをフルスピードで送り出せるようになるのだ。
2. パケットレベルで視る「接続再利用」の挙動
`Keep-Alive` が有効な場合、カーネル内のソケットバッファはクリアされず、次のHTTPリクエストを待機する。ここで重要なのが、Linuxカーネルの `tcp_keepalive` パラメーターとの対話だ。
もしサーバーサイドで `keepalive_timeout` を短く設定しすぎると、無駄な `FIN` パケットが飛び交い、接続が頻繁に切断される。逆に長すぎれば、アイドル状態の接続がサーバーのリソース(メモリ)を食い潰す。
現場でチューニングする際は、以下のsysctlパラメーターを意識してほしい。
アイドル状態の接続に対するTCP keepaliveの挙動を最適化
接続が切れたと判断するまでの時間を短縮し、ゾンビ接続を掃討する
sysctl -w net.ipv4.tcp_keepalive_time=600 # 600秒間通信がなければプローブ開始
sysctl -w net.ipv4.tcp_keepalive_intvl=10 # プローブの間隔は10秒
sysctl -w net.ipv4.tcp_keepalive_probes=3 # 3回失敗で接続断とみなす
3. HTTP/1.1の呪縛:ヘッド・オブ・ライン・ブロッキング
しかし、`Keep-Alive` を導入してもHTTP/1.1には避けられない宿命がある。「ヘッド・オブ・ライン・ブロッキング(HOLB)」だ。
HTTP/1.1の接続では、リクエストはシリアルに処理される。つまり、前のリクエストのレスポンスがネットワーク上で詰まると、後ろに並んでいるリクエストはたとえ準備ができていても、先頭のレスポンスが完了するまで待たなければならない。
この限界を突破するために、かつてはブラウザごとに「ドメインを分けて接続数を増やす」という涙ぐましいハックが行われていたが、これはTCPウィンドウの無駄な消費とコンテキストスイッチの増大を招く、あまりに非効率な手法だった。現代のアーキテクトなら、HTTP/2の多重化(Multiplexing)へ移行すべきだが、レガシーシステムの維持において `Keep-Alive` のチューニングは依然として生命線である。
4. セキュリティとパフォーマンスのトレードオフ
`Keep-Alive` は便利だが、セキュリティの観点では「リソース枯渇攻撃(DoS)」の標的になりやすい。
多くのリクエストを1つの接続で処理できることは、攻撃者が少数の接続でサーバーのワーカースレッドを長時間占有できることを意味する。これを防ぐためには、Nginxのようなリバースプロキシで適切に制限をかけるのが鉄則だ。
Nginx設定例:接続リソースの保護
http {
# 同一接続内で処理する最大リクエスト数
keepalive_requests 1000;
# 接続を維持する最大時間
keepalive_timeout 65s;
# 低速なクライアントからの攻撃を防ぐためのタイムアウト設定
client_body_timeout 10s;
client_header_timeout 10s;
}
結び:パケットの向こう側を設計する
`Connection: keep-alive` は、単なるヘッダーではない。ネットワーク資源という有限なパイプを、いかにして効率よく、そして安全に使い回すかという、インフラエンジニアの知恵の結晶だ。
現代のネットワークアーキテクチャでは、TLS 1.3がハンドシェイク時間を劇的に短縮し、HTTP/3 (QUIC) がUDPベースでHOLBを解消している。しかし、HTTP/1.1の「接続を再利用する」という概念は、あらゆるプロトコルの根底に流れている。
パケットがNICを通過し、カーネルのスタックを登り、アプリケーションへ届くまでの経路を想像せよ。その一瞬一瞬に、我々の設計したパラメーターが息づいている。それこそが、インフラエンジニアとしての醍醐味ではないだろうか。
コメント