ラボでサーバーのベンチマークを実施し、実際のトラフィック環境に導入したところ、ベンチマーク時のパフォーマンスには遠く及ばなかったことはありませんか。CPU使用率は低く、空きリソースも十分にあるにもかかわらず、クライアントから応答時間が遅いと指摘され、サーバーの使用率を高める方法がわからないという状況です。
現在見られる現象は、「HTTPの高負荷処理」と呼べるものがもたらす影響の1つです。このブログ記事では、HTTPの仕組みと、一般的なHTTPサーバーがHTTPトランザクションを処理する方法を検証します。また、発生し得るパフォーマンス上の問題を取り上げ、NGINXのイベント駆動型モデルが、これらのHTTPサーバーに対する高速化プロキシとして非常に効果的である理由を解説します。NGINXを使用すれば、実環境のパフォーマンスをローカル環境でのベンチマークと同等のレベルまで引き上げることができます。
LinuxとNGINXをチューニングしてアプリケーションの速度とスケーラビリティを向上させるためのヒントについては、F5のブログ記事「パフォーマンス向上のためのNGINXチューニング」をご覧ください。
HTTPとキープアライブ接続の概要
HTTP Keep-Alive接続は、レイテンシーを低減し、Webページの読み込みを高速化するために必要なパフォーマンス機能です。
HTTPは、シンプルなテキストベースのプロトコルです。まだ確認したことがない場合は、Webブラウザーに搭載されているものなど、HTTPデバッグツールの出力を開き、標準的なリクエストとレスポンスの構造を確認してください。

最も単純な実装では、HTTPクライアントは宛先サーバーへの新しいTCP接続を確立し、リクエストを送信してレスポンスを受信します。その後、サーバーはリソースを解放するためにTCP接続を閉じます。

この動作モードは、特に多数の要素を含む複雑なWebページや、ネットワークリンクが低速な場合には、非常に非効率になる可能性があります。新しいTCP接続を確立するには「3ウェイハンドシェイク」が必要で、切断時にも2ウェイのシャットダウン手順が伴います。メッセージごとにTCP接続の確立と切断を繰り返すのは、電話で一人が話すたびに通話を切り、かけ直すようなものです。
HTTPでは、「キープアライブ接続」と呼ばれる仕組みを使用し、HTTPトランザクションの完了後もクライアントとサーバー間のTCP接続を維持します。クライアントが別のHTTPトランザクションを実行する必要がある場合、新しいTCP接続を確立する代わりに、アイドル状態のキープアライブ接続を使用できます。

クライアントは通常、サーバーに対して複数のTCP接続を同時に確立し、そのすべてでキープアライブ通信を行います。これらの接続は、通常はアイドルタイムアウトにより、クライアントまたはサーバーが不要と判断するまで維持されます。
最新のWebブラウザは通常、6~8本のキープアライブ接続を開き、タイムアウトするまで数分間維持します。Webサーバーでは、これらの接続をより早くタイムアウトさせて閉じるように設定できます。
KeepaliveはHTTPサーバーにどのような影響を与えるか
多数のクライアントがHTTPキープアライブを使用しており、Webサーバーに同時接続数の上限やスケーラビリティの問題がある場合、その上限に達するとパフォーマンスが急激に低下します。
上記のアプローチは、個々のクライアントで可能な限り高いパフォーマンスを実現するよう設計されています。しかし、「共有地の悲劇」のように、すべてのクライアントがこの方法で動作すると、一般的な多くのWebサーバーやWebアプリケーションのパフォーマンスに悪影響を及ぼす可能性があります。
これは、多くのサーバーで同時接続数の上限が固定されているためです。たとえば、一般的な構成では、Apache HTTP Serverが同時に処理できるTCP接続数は、workerマルチプロセッシングモジュール(MPM)では150、prefork MPMでは256に制限されます。アイドル状態のHTTPキープアライブ接続も、この同時接続枠を1つ消費します。すべての枠が使用されると、サーバーはそれ以上HTTP接続を受け付けられなくなります。
一般的には、Webサーバーでキープアライブを無効にするか、有効期間を非常に短く制限することが推奨されます。キープアライブは、SlowHTTPTestやSlowlorisによるサービス拒否攻撃の非常に単純な攻撃経路となります(迅速な対策については、serverfault.comの「Keep-Deadサービス拒否攻撃からの保護」を参照してください)。
さらに、これらのWebサーバーやアプリケーションサーバーは通常、接続ごとにオペレーティングシステムのスレッドまたはプロセスを割り当てます。TCP接続は非常に軽量なオペレーティングシステムオブジェクトですが、スレッドやプロセスは非常に多くのリソースを消費します。スレッドやプロセスにはメモリが必要であり、オペレーティングシステムによる能動的な管理も必要です。また、スレッド間またはプロセス間の「コンテキストスイッチ」はCPUリソースを消費します。接続ごとに専用のスレッドまたはプロセスを割り当てる方法は、極めて非効率的です。

多数のクライアント接続が同時に発生し、接続ごとにスレッドまたはプロセスが割り当てられることで、「HTTP heavy lifting」と呼ばれる現象が生じます。これは、軽量なHTTPトランザクションの処理に不釣り合いなほど多くのリソースが必要になる現象です。
実際には何を意味するのか
現在の多くのWebサーバーやアプリケーションサーバーでは、少数のクライアントでも同時接続数の上限に達します。
クライアントが8本のTCP接続を開き、最後に使用してから15秒間それぞれの接続を維持すると、そのクライアントは15秒間、8つの同時接続スロットを使用します。Webサイトに毎秒1クライアントの割合でアクセスがある場合、アイドル状態のキープアライブ接続によって120の同時接続スロットが常に占有されます。毎秒2クライアントの場合は、240の同時接続スロットが占有されます。スロットがすべて使用されると、既存の接続がタイムアウトするまで、新しいクライアントは接続できません。
その結果、サービスレベルに大きなばらつきが生じる可能性があります。キープアライブ接続を確立できたクライアントは、サービスを自由に閲覧できます。一方、すべての同時接続スロットが使用中のときに接続を試みたクライアントは接続できず、キューで待機する必要があります。
ベンチマークテストでは、なぜこのような影響が見られないのでしょうか?
これらの問題は、多数のクライアントが接続する低速なネットワークでのみ顕在化します。高速なローカルネットワーク上で単一のクライアントを使用してベンチマークテストを行う場合には発生しません。
ベンチマークでは、これらの影響が確認できない場合があります。これにはいくつかの理由があります。
- ベンチマーク中にキープアライブを有効にしない場合、クライアントはトランザクションごとに新しいTCP接続を確立します(トランザクションの完了後に接続は切断されます)。ベンチマークは高速なローカルネットワーク上で実行されることが多いため、正常に完了し、キープアライブを使用しないことで生じるパフォーマンス上の問題は表面化しません。
- ベンチマーク中にキープアライブを有効にした場合、サーバーの上限より少ない同時接続数で実行できる可能性が高く、ベンチマーククライアントが各接続を繰り返し使用して飽和させるため、サーバーは最大容量に達します。ただし、これは実環境の接続プロファイルとは異なります。
ほとんどのベンチマークツールは、成功したトランザクションのみをレポートする点に注意してください。リソースの枯渇によって停滞した接続はレポートされないか、成功した接続のごく一部にすぎないように見える場合があります。そのため、実際のトラフィックで生じる問題の本質が見えにくくなります。
この問題はどの程度一般的か?
スレッドベースまたはプロセスベースのWebサーバーやアプリケーションサーバーは、いずれも同時実行数の制限による影響を受ける可能性があります。
この問題は、接続ごとにスレッドまたはプロセスを割り当てるあらゆるWebプラットフォームやアプリケーションプラットフォームに内在します。最適化されたベンチマーク環境では容易に検出できませんが、実環境ではパフォーマンスの低下やCPU使用率の過度な上昇として顕在化します。
この問題に対処するには、いくつかの方法があります。
- スレッドまたはプロセスの数を増やす – これはごく短期的な対策です。スレッドとプロセスは負荷の大きいオペレーティングシステムオブジェクトであり、生成数が増えるにつれて管理オーバーヘッドが急速に増大します。
- HTTPキープアライブを無効にするか、使用を制限する – 同時接続数の上限に達するまでの時間を延ばせますが、各クライアントのパフォーマンスが大幅に低下します。
- 専用のキープアライブ処理を使用する – Apache HTTP Server(Webサーバー)には、比較的新しいevent MPMがあります。このMPMは、接続が「アクティブ」状態と「アイドルキープアライブ」状態の間で移行する際に、接続をワーカースレッドと専用のイベントスレッドの間で移動します。使用している他のモジュールがこのMPMをサポートしている場合は、選択肢となる可能性があります。ただし、SSL/TLS接続は引き続き、すべて専用スレッドで処理される点に注意してください。
- より効率的な処理モデルを使用する – 最も簡単で効果的な対策は、Webサーバーまたはアプリケーションサーバーの前段に、効率的なHTTPプロキシを配置することです。NGINXのようなイベント駆動型プロキシには、前述の同時実行数の制限がありません。低速な接続やアイドル状態のキープアライブも難なく処理できます。さらに、複数のアイドル状態のキープアライブ接続を伴う低速なクライアント側接続を、高速かつローカルで効率性の高いベンチマーク形式の接続へと効果的に変換し、Webサーバーやアプリケーションサーバーから最大限のパフォーマンスを引き出します。
NGINXを高速化HTTPプロキシとして使用する
NGINXは、前述の同時実行に関する問題の影響を受けない、異なるアーキテクチャを採用しています。低速なクライアント接続を、ベンチマーク環境のように最適化された接続へと変換し、サーバーのパフォーマンスを最大限に引き出します。
NGINXは、非常に効率的なイベント駆動モデルを使用して接続を管理します。
各NGINXプロセスは、複数の接続を同時に処理できます。新しい接続を受け入れる際のオーバーヘッドは、新しいファイルディスクリプターとポーリング対象の新しいイベントのみで構成されるため、前述のプロセス単位またはスレッド単位のモデルとは異なり、非常に小さく抑えられます。NGINXには、非常に効率的なイベントループがあります。

これにより、各NGINXプロセスは、数十、数千、さらには数十万の同時接続にも容易にスケールできます。
その後、NGINXはローカルのキープアライブ接続プールを使用して、リクエストをアップストリームサーバーにプロキシします。TCP接続の確立と切断に伴うオーバーヘッドが発生せず、TCPスタックは最適なウィンドウサイズと再試行パラメーターにすばやく適応します。最適化されたローカルネットワークでは、リクエストの送信とレスポンスの読み取りが大幅に高速化されます。

その結果、アップストリームサーバーは、高速なネットワークを介して単一のローカルクライアント(NGINX)と通信することになります。NGINXはHTTPキープアライブ接続を最適に活用し、接続を不必要に開いたままにすることなく、接続確立の処理を最小限に抑えます。これにより、サーバーはベンチマーク環境に近い最適な状態に戻ります。
NGINXをHTTPプロキシとして使用すると、次のような効果が得られます。
- 既存リソースをより有効に活用できます。HTTPの負荷が高い処理を担う必要がなくなるため、Webサーバーとアプリケーションサーバーは1秒あたりにより多くのトランザクションを処理できます。
- エラー率の低減。NGINXがすべてのクライアントの中央スケジューラーとして機能するため、HTTPタイムアウトが発生する可能性が大幅に低下します。
- エンドユーザーのパフォーマンスが向上。サーバーの稼働効率が高まり、接続をより迅速に処理できます。
NGINXでサービスを高速化するその他の方法
HTTPの負荷が高い処理を肩代わりすることは、過負荷状態にあるアプリケーションインフラストラクチャのパフォーマンスを大きく改善するために、NGINXが提供できる対策の1つにすぎません。
NGINXの「HTTPキャッシュ」機能は、標準的なキャッシュセマンティクスに従って、キャッシュする対象と期間を制御しながら、アップストリームサーバーからのレスポンスをキャッシュできます。複数のクライアントが同じリソースをリクエストした場合、NGINXはキャッシュからレスポンスを返すことで、重複するリクエストによるアップストリームサーバーの負荷を回避できます。
NGINXでは、アップストリームサーバーからほかの処理をオフロードすることもできます。帯域幅の使用量を削減するためのデータ圧縮処理をオフロードし、SSL/TLSの暗号化と復号を一元化できます。また、HTTP基本認証、外部認証サーバーへのサブリクエスト、JSON Web Tokenなどを使用して初期クライアント認証を実行し、必要に応じてさまざまなルールを適用してトラフィックのレートを制限できます。
一般的なロードバランサーやADCとは一線を画す
最後に、NGINXは、他の高速化プロキシ、ロードバランサー、アプリケーションデリバリーコントローラー(ADC)とは異なり、完全なWebサーバーでもあることを忘れてはなりません。NGINXを使用すると、静的コンテンツの配信、Java、PHP、Python、Rubyなどの言語に対応するアプリケーションサーバーへのトラフィック転送、メディア(音声および動画)の配信、認証システムやセキュリティシステムとの統合が可能です。さらに、NGINXの設定に組み込まれたルールを使用して、トランザクションに直接応答することもできます。
NGINXとNGINX Plusにはパフォーマンス上の制限が組み込まれていないため、現在も将来も、導入先のハードウェアを最大限に活用できます。
NGINX Plusをお試しいただくには、30日間の無料トライアルを今すぐ開始するか、ユースケースについてご相談ください。
著者について

関連するブログ記事
チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。
AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。
JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。
F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。
NGINX PlusによるAWS Auto Scalingグループの負荷分散
新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります