2010年代には Apache の mpm_prefork とか流行らない (もしくは HTTP keep-alive のメリットとデメリット)

HTTP の持続的接続の功罪について

はじめに、HTTP の持続的接続 (keep-alive) のメリットについて。持続的接続を使うメリットは、以下の2点。

  • TCP 接続の確立にかかる時間の節約*1
  • TCP の接続と切断に必要な資源 (CPUとネットワーク) の節約

ウェブブラウザ〜データセンタ間の通信で、持続的接続を使う理由は、このうちの前者。特に太平洋を超えるようなケースだと、TCP 接続に0.2秒とかかかるので、メリットが大きい。

一方、持続的接続のデメリットは、

  • 接続が切断されるまでの間、その接続を維持するためにコストがかかる (主としてメモリが無駄になる)

という点になる。特に、1プロセス1コネクションを前提とするアーキテクチャ (例: mod_perl) だと、メモリの無駄使いが、とてもひどいことになる。

そこで、ブラウザからの接続を受ける HTTP サーバとアプリケーションサーバを分割し、前者には、イベントドリブン、あるいはマルチスレッドの httpd を使うことになる。前者は同時接続数 500 で後者は 50。とかそんなそんな感じで設定する。

両者を分割する理由は、それだけじゃないけど*2、ここでは割愛。

HTTP サーバとアプリケーションサーバの接続プロトコル

HTTP, FastCGI あたりがメジャーでしょうか。CGI プロトコル (環境変数+パイプ通信) も、ここに入れちゃっていいと思う。HTTP プロトコルを用いて両者を接続する場合には、ブラウザからの接続をアプリケーションサーバに中継する方の HTTP サーバを「リバースプロキシ」と呼ぶのが一般的。

最近は、たいていのプログラミング言語において、そこそこ効率のいい HTTP サーバがあるので、それを使ってアプリケーションサーバを構築すると運用が簡単になる。Java ならば、Jetty 組み込みの httpd は実績が豊富だし (ちょっとコードが煩雑だと思うけど)、Perl だと Starman とか Starlet とか。

アプリケーションをスクリプト言語で開発している場合だと、以前はアプリケーションサーバとして、Apache (mpm_prefork+mod_perl等) を使うケースも多かったと思うけど、Apacheスクリプト言語に跨がるバッドノウハウがいろいろ必要になるので、今後は流行らないんじゃないかなと思ってる (PHP を除く)。

個人的には、Perl 界隈でも最近は FastCGI が流行らなくなってきてるのが、なかなかうれしい今日この頃です (参考: FastCGI プロトコルってなんのためにあるのかわからない - kazuhoのメモ置き場)。

リバースプロキシ〜アプリケションサーバ間の接続に持続的接続を使うべきか

閑話休題

先に書いたように、持続的接続のメリットは、時間 (レイテンシ) と、CPUおよびネットワーク資源の節約にある。だが、リバースプロキシとアプリケーションサーバは同一データセンタ内に配置するわけだから、レイテンシを気にする必要はない。ネットワーク資源についても、TCP の接続切断に必要なパケットサイズは小さいし、データセンタ内通信に課金されることは普通ないから問題ない。あとは CPU 資源だけど、アプリケーションサーバで行う処理 (動的ページの構築) と比べたら TCP の接続切断処理のオーバーヘッドは微々たるもの (であることがほとんど) なので、通常は問題にならない*3

逆に、持続的接続を有効にしちゃうと、しょうもない問題が発生したりする (例: Apache2+mod_proxy+持続接続で時々レスポンスが悪くなる現象のメモ - 北海道苫小牧市出身の初老PGが書くブログ とか linux の TCP_DEFER_ACCEPT (サーバサイド) の挙動について - kazuhoのメモ置き場 のロードバランサの例とか) ので、避けるべき。

そんなこんなの理由から、アプリケーションサーバhttpd として使うことしか考えてない Starlet (旧 Plack::Server::Standalone::Prefork(::Server::Starter) ) は、デフォルトで keep-alive をオフにしてる。

まとめというか余談

mpm_prefork って使いどころなくなってきてるなー。PHP の人たちは今後どうするんだろ。

*1:HTTP Pipelining もあるけど、論点が拡散するので省略

*2:アプリケーションサーバは10台くらい必要だけど、ブラウザからのリクエストは単一の IP アドレスで受けたいとか、静的コンテンツの配信をアプリケーションサーバよりも台数が少なくて良いフロントの HTTP サーバから行ったほうが、デプロイコストが下がるとか

*3:静的コンテンツをアプリケーションサーバにデプロイする場合でも、前段の http サーバにキャッシュさせるべきだし、そうしちゃえば TCP 接続負荷は問題にならない (参考: Plack::Server::Standalone 系を使ってウェブアプリケーション開発と運用が楽になる話 - JPerl Advent Calendar 2009)