「なぜその接続は切れないのか」—— HTTP Keep-Aliveが支えるWebの効率性と実務の落とし穴
Webエンジニアとしてキャリアを積んでいると、一度は「なぜリクエストのたびにハンドシェイクをしていてはダメなのか」という疑問に突き当たるはずだ。
HTTP/1.0の時代、TCPコネクションは「リクエストごとに確立し、レスポンスを受け取ったら即座に切断する」のが基本ルールだった。しかし、モダンなWebサイトは数十から数百の小さな画像やスクリプトを読み込む。毎回TCPの3ウェイ・ハンドシェイク(SYN, SYN-ACK, ACK)を繰り返すことは、往復遅延時間(RTT)を無駄に積み上げ、サーバー側に大量のTIME_WAIT状態を生成する「インフラ殺し」の所業だ。
そこで登場したのが `Connection: keep-alive`。今回は、このWeb通信の「生命線」とも言える仕組みを、現場の視点から深掘りしていこう。
—
1. Keep-Aliveのメカニズム:コネクションを「使い回す」知恵
HTTP/1.1から「デフォルトで永続接続(Persistent Connection)」が標準となったが、その挙動を司るのが `Connection` ヘッダーだ。
クライアントがリクエストヘッダーに `Connection: keep-alive` を含めると、サーバーはレスポンス送信後もすぐにTCP接続を閉じず、クライアントからの次のリクエストを待機する。これにより、クライアントは確立済みのパイプラインを再利用できる。
シーケンスの比較
- Keep-Aliveなし: `[SYN] → [SYN-ACK] → [ACK] → [HTTP Req] → [HTTP Res] → [FIN] → [ACK]`(毎リクエスト発生)
- Keep-Aliveあり: `[SYN] → [SYN-ACK] → [ACK] → [HTTP Req 1] → [HTTP Res 1] → [HTTP Req 2] → [HTTP Res 2] → [FIN] → [ACK]`(接続確立コストを一度に圧縮)
—
2. 現場で意識すべきパラメーター:TimeoutとMax
ただ「繋ぎっぱなし」にすれば良いわけではない。サーバーリソースには限りがある。そこで登場するのが `Keep-Alive` レスポンスヘッダーだ。
Keep-Alive: timeout=5, max=1000
- timeout: 次のリクエストが来るまでサーバーが接続を保持する秒数。短すぎればオーバーヘッドが増え、長すぎればアイドル接続でメモリを浪費する。
- max: 一つのコネクションで処理できる最大リクエスト数。これが上限に達すると、サーバーは `Connection: close` を返して強制的に切断する。
インフラ運用では、この値をNginxやApacheの設定で適切にチューニングすることが、高トラフィック時のサーバー負荷を制御する鍵となる。
—
3. 実践:Keep-Aliveを操るコードと確認術
Python (requests) での挙動確認
requestsライブラリはデフォルトで `Session` オブジェクトを使うと自動的にKeep-Aliveが有効になる。
import requests
Sessionを使うことで、内部的にコネクションプールが維持される
session = requests.Session()
1回目のリクエスト(ここでハンドシェイクが発生)
session.get(‘https://api.example.com/data’)
2回目のリクエスト(既存のTCP接続を再利用する)
session.get(‘https://api.example.com/data’)
curl でのデバッグ手法
現場で「ちゃんとコネクションが使い回されているか」を確認するには、`curl` の `–trace-ascii` オプションが最強だ。
接続の過程を覗き見る
curl -v –trace-ascii – https://www.google.com
出力結果に `Re-using existing connection` という文字列が見えれば、Keep-Aliveが正しく機能している証拠だ。
—
4. トラブルシューティングの勘所
シニアエンジニアとして、後輩によく伝える「ハマりどころ」がいくつかある。
1. プロキシ越しの挙動: 間に立つロードバランサー(ALBやNginx)が、バックエンドへの接続を強制的に切断していないか? `Keep-Alive` を維持するためには、クライアントからバックエンドまで一貫した設定が必要だ。
2. ブラウザの挙動: Chromeなどの現代的なブラウザは、同一ドメインに対して最大6つ程度のTCP接続を並行して開く(ブラウザの仕様)。Keep-Aliveが効いていても、並列数を超えれば新しいコネクションが生成されることは忘れてはならない。
3. HTTP/2の存在: もし可能なら、HTTP/1.1のKeep-Aliveに拘泥するよりも、HTTP/2への移行を推奨する。HTTP/2は「多重化(Multiplexing)」によって、単一のTCP接続上で無数のリクエストを同時に捌ける。これぞ究極のKeep-Aliveの進化形と言える。
—
最後に:ネットワークを「流れる」意識を持つこと
Keep-Aliveは単なるヘッダーの文字列ではない。それはサーバーとクライアントの間で交わされる「まだ用事があるから、もう少しだけ待ってくれ」という暗黙の合意だ。
ネットワークの世界に魔法はない。すべての通信にはコストがかかり、すべての接続には寿命がある。その挙動を理解し、適切に制御できるエンジニアこそが、真に堅牢で高速なWeb APIを設計できると私は信じている。
もしあなたの構築したシステムでレスポンスが遅延しているなら、まずは `TCP SYN` が頻発していないか、コネクションプールが適切に枯渇していないか、パケットキャプチャを広げて確認してみてほしい。そこには必ず、解決のヒントが流れているはずだ。
コメント