更新、2018年春:
私は2010年にこの回答を書きましたが、それ以来、Webバックエンド開発者の世界では多くのことが変化しました。つまり、ワンクリックロードバランサーや商品への自動スケーリングなどの「クラウド」ターニングサービスの出現により、アプリケーションのスケーリングの実際の仕組みをはるかに簡単に開始できるようになりました。
とは言うものの、2010年にこの記事で書いたことは今日でもほとんど当てはまり、Webサーバーと言語ホスティング環境が実際にどのように機能するか、そしてそれを調整する方法の背後にあるメカニズムを理解することで、ホスティングコストを大幅に節約できます。そのため、スタックの調整にひじを深く入れ始めている人のために、最初に以下に書いた記事を残しました。
1. Webサーバー(および場合によってはそのような構成)に依存します。さまざまなモデルの説明:
mpm_preforkを使用したApache(UNIXのデフォルト):リクエストごとに処理します。起動時間を最小限に抑えるために、Apacheはアイドル状態のプロセスのプールを保持して(サイズを構成する)新しい要求を処理するのを待機します。新しいリクエストが着信すると、マスタープロセスはそれを利用可能なワーカーに委任します。それ以外の場合は、新しいリクエストを生成します。100のリクエストが入った場合、アイドル状態のワーカーが100人いない限り、負荷を処理するためにフォークを実行する必要があります。アイドル状態のプロセスの数がMaxSpare値を超えると、要求の終了後、アイドル状態のプロセスの数が非常に多くなるまで、一部のプロセスが刈り取られます。
mpm_event、mpm_worker、mpm_winntを使用したApache:リクエストごとのスレッド。同様に、apacheはほとんどの状況でアイドル状態のスレッドのプールを保持し、構成可能です。(細かい部分ですが、機能的には同じです。mpm_workerは複数のプロセスを実行し、それぞれがマルチスレッド化されています)。
Nginx / Lighttpd:これらは軽量のイベントベースのサーバーであり、select()/ epoll()/ poll()を使用して、複数のスレッドやプロセスを必要とせずに多数のソケットを多重化します。非常に注意深いコーディングと非ブロッキングAPIの使用により、利用可能な帯域幅と正しく構成されたファイル記述子の制限を提供して、コモディティハードウェアでの数千の同時リクエストに拡張できます。注意点は、従来の埋め込みスクリプト言語をサーバーコンテキスト内で実装することはほとんど不可能であり、これはほとんどの利点を打ち消すことになります。ただし、どちらも外部スクリプト言語に対してFastCGIをサポートしています。
2.使用するデプロイメントモデルの言語、または一部の言語によって異なります。一部のサーバー構成では、特定の展開モデルのみが許可されます。
Apache mod_php、mod_perl、mod_python:これらのモジュールは、Apacheワーカーごとに個別のインタープリターを実行します。これらのほとんどは、mpm_workerではうまく機能しません(クライアントコードのスレッドの安全性に関するさまざまな問題のため)。したがって、ほとんどの場合、モデルのフォークに限定されます。つまり、各Apacheプロセスに対して、php / perl/pythonインタープリターが内部で実行されているということです。これにより、メモリフットプリントが大幅に増加します。特定のapacheワーカーが通常システムで約4MBのメモリを使用する場合、平均的なアプリケーションでは、PHPを使用するものは15MB、Pythonを使用するものは20〜40MBを使用する可能性があります。この一部はプロセス間で共有メモリになりますが、一般に、これらのモデルを非常に大規模に拡張することは非常に困難です。
Apache(サポートされている構成)、Lighttpd、CGI:これは主にホスティングの完全な方法です。CGIの問題は、要求を処理するための新しいプロセスをフォークするだけでなく、負荷を増やす必要があるときだけでなく、すべての要求に対してフォークすることです。今日の動的言語の起動時間はかなり長いため、これによりWebサーバーに多くの作業が発生するだけでなく、ページの読み込み時間が大幅に増加します。小さなperlスクリプトはCGIとして実行するのに問題がないかもしれませんが、大きなpython、ruby、またはjavaアプリケーションはかなり扱いにくいです。Javaの場合、アプリの起動だけで1秒以上待機している可能性がありますが、次のリクエストですべてをやり直す必要があります。
すべてのWebサーバー、FastCGI / SCGI / AJP:これは、動的言語を実行するための「外部」ホスティングモデルです。興味深いバリエーションの全リストがありますが、要点は、アプリケーションがある種のソケットでリッスンし、WebサーバーがHTTP要求を処理してから、動的ページ(静的ページは通常、Webサーバーによって直接処理されます)。
これにより、接続を処理する機能よりも動的なワーカーが少なくて済むため、多くの利点が得られます。100リクエストごとに、半分が画像やCSSなどの静的ファイル用であり、さらにほとんどの動的リクエストが短い場合は、100の同時クライアントを処理する20の動的ワーカーでうまくいく可能性があります。つまり、特定のWebサーバーのキープアライブ接続の通常の使用は80%アイドルであるため、動的インタープリターは他のクライアントからの要求を処理できます。これは、ユーザーがCSSファイルをロードしているとき、または何もロードしていないときに、インタープリターがメモリを使用して作業を行わずにそこに座っているmod_php / python/perlアプローチよりもはるかに優れています。
Apache mod_wsgi:これは特にPythonのホスティングに適用されますが、Webサーバーでホストされるアプリ(簡単な構成)と外部ホスティング(プロセスの多重化)の利点のいくつかを利用します。デーモンモードで実行すると、mod_wsgiは必要な場合にのみデーモンワーカーにリクエストを委任するため、4つのデーモンが100人の同時ユーザーを処理できる可能性があります(サイトとそのワークロードによって異なります)
Phusion Passenger:Passengerは、主にrubyアプリをホストするためのApacheホスティングシステムであり、mod_wsgiと同様に、外部ホスティングとWebサーバー管理ホスティングの両方の利点を提供します。
3.繰り返しになりますが、これが適用できる場合は、ホスティングモデルに基づいて質問を分割します。
mod_php、mod_python、mod_perl:通常、アプリケーションのCライブラリのみがApacheワーカー間で共有されます。これは、apacheが最初にフォークし、次に動的コードをロードするためです(微妙なため、ほとんどの場合、共有ページを使用できません)。通訳者は、このモデル内で相互に通信しません。通常、グローバル変数は共有されません。mod_pythonの場合、プロセス間ではなく、プロセス内のリクエスト間でグローバルを保持できます。これにより、非常に奇妙な動作が発生する可能性があります(ブラウザーが同じ接続を永久に維持することはめったになく、特定のWebサイトに対していくつかを開くことがほとんどです)。したがって、グローバルの使用方法には十分注意してください。共有する必要のあるセッションストレージやその他のキャッシュビットなどには、memcachedやデータベースまたはファイルなどを使用します。
FastCGI / SCGI / AJP / Proxied HTTP:アプリケーションは本質的にそれ自体がサーバーであるため、これはサーバーが記述されている言語(通常はコードと同じ言語ですが、常にではありません)とさまざまな要因によって異なります。たとえば、ほとんどのJavaデプロイメントは、要求ごとのスレッドを使用します。Pythonとその「フロップ」FastCGIライブラリは、プリフォークモードまたはスレッドモードのいずれかで実行できますが、PythonとそのGILには制限があるため、プリフォークから最高のパフォーマンスが得られる可能性があります。
mod_wsgi /passenger:サーバーモードのmod_wsgiは、処理方法を構成できますが、プロセスの数を固定することをお勧めします。Pythonコードをメモリに保持し、スピンアップして準備を整えたいと考えています。これは、レイテンシを予測可能かつ低く保つための最良のアプローチです。
上記のほとんどすべてのモデルでは、プロセス/スレッドの存続期間は単一のリクエストよりも長くなります。ほとんどのセットアップは、apacheモデルのいくつかのバリエーションに従います。いくつかの構成可能な制限に基づいて、予備のワーカーを維持し、必要に応じてさらにスポーンし、多すぎる場合は刈り取ります。これらのセットアップのほとんどは、リクエスト後にプロセスを破棄しませんが、アプリケーションコードをクリアする場合もあります(PHP fastcgiの場合など)。
4.「Webサーバーは100の要求しか処理できない」と言う場合、それは実際のWebサーバー自体を意味するのか、Webサーバーの動的部分を意味するのかによって異なります。実際の制限と機能の制限にも違いがあります。
たとえば、Apacheの場合、ワーカー(接続)の最大数を構成します。この接続数が100に達して到達した場合、誰かが切断するまで、apacheはそれ以上接続を受け入れません。キープアライブを有効にすると、これらの100の接続は、単一のリクエストよりもはるかに長く開いたままになる可能性があり、リクエストを待機している他の900人はおそらくタイムアウトになります。
制限が十分に高い場合は、それらすべてのユーザーを受け入れることができます。ただし、最も軽量なapacheを使用した場合でも、コストはワーカーあたり約2〜3 mbです。したがって、apacheだけを使用すると、プロセスID、ファイル記述子、とバッファ、そしてこれはあなたのアプリケーションコードを検討する前です。
lighttpd / Nginxの場合、小さなメモリフットプリントで多数の接続(数千)を処理できます。多くの場合、1000接続あたりわずか数メガバイトです(バッファや非同期IO APIの設定方法によって異なります)。ほとんどの接続が維持され、80%(またはそれ以上)がアイドル状態であると仮定すると、動的なプロセス時間や大量のメモリを浪費しないため、これは非常に優れています。
外部でホストされているモデル(mod_wsgi / fastcgi / ajp / proxied http)では、ワーカーが10人で、ユーザーが1000人の場合、Webサーバーは動的ワーカーへのリクエストをキューに入れます。これは理想的です。リクエストがすぐに返される場合は、より多くのワーカーを必要とせずに、はるかに大きなユーザー負荷を処理し続けることができます。通常、プレミアムはメモリまたはDB接続であり、キューイングすることで、一部のユーザーを拒否するのではなく、同じリソースでより多くのユーザーにサービスを提供できます。
注意:レポートを作成したり検索を行ったりして数秒かかる1つのページがあり、多くのユーザーがこれでワーカーを拘束しているとします。フロントページをロードしたい人は、数秒間キューに入れられる可能性があります。長時間実行されるリクエストが完了しました。別の方法としては、別のワーカープールを使用して、レポートアプリセクションへのURLを処理するか、(バックグラウンドジョブのように)レポートを個別に実行して、後でその完了をポーリングします。そこにはたくさんのオプションがありますが、アプリケーションにいくつかの考慮を払う必要があります。
5.メモリフットプリントが大きいために多数の同時ユーザーを処理する必要があるapacheを使用しているほとんどの人は、keep-aliveをオフにします。または、キープアライブがオンになっているApacheで、キープアライブの制限時間が短く、たとえば10秒です(1ページの読み込みでフロントページと画像/ CSSを取得できます)。本当に1000以上の接続に拡張する必要があり、維持したい場合は、Nginx/lighttpdやその他の軽量のイベントベースのサーバーを検討することをお勧めします。
(構成を使いやすくするため、または特定のセットアップをホストする必要があるために)Apacheが必要な場合は、HTTPプロキシを使用してNginxをApacheの前に置くことができます。これにより、Nginxはキープアライブ接続(および、できれば静的ファイル)を処理し、apacheはうなり声の作業のみを処理できるようになります。興味深いことに、Nginxはログファイルの書き込みにおいてApacheよりも優れています。実稼働環境では、apacheの前にあるnginx(この場合はmod_wsgi)に非常に満足しています。apacheはアクセスログを実行せず、静的ファイルも処理しないため、apache内の多数のモジュールを無効にして、フットプリントを小さく保つことができます。
私はすでにこれにほとんど答えましたが、いいえ、接続が長い場合は、インタプリタの実行時間に関係する必要はありません(外部でホストされているアプリケーションを使用している限り、これは明らかです。非常に優れています)。したがって、cometを使用したい場合、および長いキープアライブ(これを処理できる場合は通常は良いことです)を使用する場合は、nginxを検討してください。
ボーナスFastCGIの質問 fastcgiは単一の接続内で多重化できるとおっしゃいました。これは確かにプロトコルによってサポートされているため(この概念は「チャネル」として知られていると思います)、理論的には1つのソケットで多くの接続を処理できます。ただし、これはfastcgi実装者の必須機能ではなく、実際には、これを使用する単一のサーバーはないと思います。これを実装するのは非常に難しいため、ほとんどのfastcgiレスポンダーもこの機能を使用しません。ほとんどのWebサーバーは、特定のfastcgiソケットを介して一度に1つの要求のみを行い、次にそのソケットを介して次の要求を行います。そのため、多くの場合、プロセス/スレッドごとに1つのfastcgiソケットしかありません。
fastcgiアプリケーションが処理またはスレッド化を使用するかどうか(および接続を受け入れて委任する「マスター」プロセスを介して実装するか、またはそれぞれが独自のことを行う多くのプロセスを介して実装するか)はあなた次第です。プログラミング言語とOSの機能によっても異なります。ほとんどの場合、ライブラリが使用するデフォルトは何でも問題ありませんが、パラメータのベンチマークと調整を行う準備をしてください。
共有状態に関しては、インプロセス共有状態の従来の使用法は存在しないふりをすることをお勧めします。それらが現在機能している場合でも、後で動的ワーカーを複数のマシンに分割する必要がある場合があります。ショッピングカートなどの状態の場合。dbが最適なオプションであり、セッションログイン情報をsecurecookiesに保持できます。一時的な状態の場合、memcachedに似たものが非常に適切です。データを共有する機能(「シェアードナッシング」アプローチ)への依存度が低いほど、将来的に拡張できる規模が大きくなります。
追記:上記のセットアップの全範囲で、多くの動的アプリケーションを作成してデプロイしました。上記のすべてのWebサーバー、およびPHP / Python / Ruby/Javaの範囲のすべてです。私は(ベンチマークと実際の観察の両方を使用して)メソッドを広範囲にテストしましたが、結果は驚くべきものになることがあります。Webサーバープロセスでのコードのホスティングから離れると、FastCGI / Mongrel / mod_wsgi/etcワーカーの数が非常に少なくなることがよくあります。アプリケーションがDBにとどまる時間によって異なりますが、CPUの数が2*を超えるプロセスでは実際には何も得られないことがよくあります。