NGINX が接続の処理に非同期のイベント駆動型アプローチを使用することはよく知られています。 つまり、従来のアーキテクチャのサーバーのように、リクエストごとに別の専用プロセスまたはスレッドを作成するのではなく、1 つのワーカー プロセスで複数の接続とリクエストを処理します。 これを実現するために、NGINX は非ブロッキング モードでソケットを操作し、 epollやkqueueなどの効率的なメソッドを使用します。
フルウェイト プロセスの数は少なく (通常は CPU コアごとに 1 つのみ)、一定であるため、消費されるメモリが大幅に少なくなり、タスクの切り替えに CPU サイクルが無駄になりません。 このようなアプローチの利点は、NGINX 自体の例を通じてよく知られています。 数百万の同時リクエストを正常に処理し、非常に適切に拡張できます。
しかし、非同期のイベント駆動型アプローチには依然として問題が残ります。 あるいは、私がよく言うように、「敵」です。 そして敵の名前は:ブロック。 残念ながら、多くのサードパーティ モジュールはブロッキング呼び出しを使用しており、ユーザー (場合によってはモジュールの開発者も) はその欠点に気づいていません。 ブロッキング操作は NGINX のパフォーマンスを低下させる可能性があるため、絶対に回避する必要があります。
現在の公式 NGINX コードでも、すべてのケースでブロック操作を回避することはできません。この問題を解決するために、 NGINX バージョン 1.7.11およびNGINX Plus リリース 7で新しい「スレッド プール」メカニズムが実装されました。 それが何であるか、そしてどのように使用されるのかについては、後で説明します。 さあ、敵と直接対決しましょう。
編集者 – NGINX Plus R7 の概要については、ブログの「NGINX Plus R7 の発表」をご覧ください。
NGINX Plus R7 のその他の新機能の詳細については、次の関連ブログ投稿を参照してください。
まず、問題をよりよく理解するために、NGINX の仕組みについて少し説明します。
一般的に、NGINX はイベント ハンドラー、つまり接続時に発生するすべてのイベントに関する情報をカーネルから受信し、オペレーティング システムに実行する処理に関するコマンドを発行するコントローラーです。 実際、NGINX はオペレーティング システムを調整することですべての面倒な作業を実行し、オペレーティング システムはバイトの読み取りと送信という日常的な作業を実行します。 したがって、NGINX が迅速かつタイムリーに応答することが非常に重要です。
イベントには、タイムアウト、読み取りまたは書き込みの準備ができているソケットに関する通知、または発生したエラーに関する通知などがあります。 NGINX は一連のイベントを受信し、それらを 1 つずつ処理して必要なアクションを実行します。 したがって、すべての処理は 1 つのスレッド内のキューに対する単純なループで実行されます。 NGINX はキューからイベントをデキューし、ソケットの書き込みや読み取りなどによってそれに反応します。 ほとんどの場合、これは非常に高速であり (メモリ内の一部のデータをコピーするのに数 CPU サイクルしかかからない可能性があります)、NGINX はキュー内のすべてのイベントを瞬時に処理します。
しかし、長時間にわたる負荷の大きい操作が行われた場合はどうなるでしょうか? この操作が完了するのを待機すると、イベント処理のサイクル全体が停止します。
したがって、「ブロッキング操作」とは、イベント処理のサイクルを長時間停止する操作を意味します。 さまざまな理由により操作がブロックされる可能性があります。 たとえば、NGINX は CPU を集中的に使用する長時間の処理でビジー状態になっている場合や、リソース (ハード ドライブ、データベースから同期的に応答を取得するミューテックスやライブラリ関数の呼び出しなど) へのアクセスを待機する必要がある場合があります。 重要な点は、このような操作を処理している間、ワーカー プロセスは他に何も実行できず、使用可能なシステム リソースがさらにあり、キュー内の一部のイベントがそれらのリソースを利用できる場合でも、他のイベントを処理できないことです。
店の前に長い列ができている店員を想像してください。 列の先頭の男性が、店内にはないが倉庫にあるものを要求します。 営業担当者は商品を配達するために倉庫へ行きます。 今では、この配達のために列全体が数時間待たなければならず、列に並んでいる全員が不満を抱いています。 人々の反応を想像できますか? 列に並んでいる人全員の待ち時間は、この時間だけ長くなりますが、購入しようとしている商品は店内にあるかもしれません。
NGINX でも、メモリにキャッシュされていないがディスクから読み取る必要があるファイルの読み取りを要求した場合に、ほぼ同じ状況が発生します。 ハード ドライブは低速であり (特に回転するもの)、キューで待機している他の要求はドライブにアクセスする必要がないかもしれませんが、いずれにしても待機を余儀なくされます。 その結果、待ち時間が増加し、システム リソースが十分に活用されなくなります。
一部のオペレーティング システムでは、ファイルの読み取りと送信のための非同期インターフェイスが提供されており、NGINX はこのインターフェイスを使用できます ( aioディレクティブを参照)。 良い例は FreeBSD です。残念ながら、Linux については同じことが言えません。 Linux はファイルを読み取るための一種の非同期インターフェースを提供していますが、これにはいくつかの重大な欠点があります。 その 1 つは、ファイル アクセスとバッファーのアラインメント要件ですが、NGINX はそれを適切に処理します。 しかし、2番目の問題はさらに深刻です。 非同期インターフェイスでは、ファイル記述子にO_DIRECT
フラグを設定する必要があります。つまり、ファイルへのアクセスはメモリ内のキャッシュをバイパスし、ハード ディスクの負荷が増加します。 それは、多くの場合、必ずしも最適とは言えません。
特にこの問題を解決するために、NGINX 1.7.11 および NGINX Plus リリース 7 でスレッド プールが導入されました。
それでは、スレッド プールとは何か、どのように機能するかについて詳しく見ていきましょう。
遠く離れた倉庫から商品を配達するかわいそうな販売員の話に戻りましょう。 しかし、彼は賢くなり(あるいは怒った顧客の群れに殴られて賢くなったのかもしれません)、配達サービスを雇いました。 今では、誰かが遠くの倉庫から何かを頼むとき、自分で倉庫に行く代わりに、配送サービスに注文を渡すだけで、配送サービスが注文を処理し、その間に当社の販売アシスタントは他の顧客への対応を続けることができます。 したがって、店舗に商品がない顧客のみが配達を待ち、他の顧客はすぐに対応できるようになります。
NGINX に関しては、スレッド プールが配信サービスの機能を実行しています。 これは、タスク キューと、キューを処理する複数のスレッドで構成されます。 ワーカー プロセスが潜在的に長い操作を実行する必要がある場合、その操作をワーカー プロセス自身で処理するのではなく、プールのキューにタスクを配置します。このキューから、任意の空いているスレッドがタスクを取得して処理できます。
どうやら別の待ち行列ができているようです。 右。 しかし、この場合、キューは特定のリソースによって制限されます。 ドライブがデータを生成できる速度よりも速くドライブから読み取ることはできません。 これで、少なくともドライブは他のイベントの処理を遅らせることはなくなり、ファイルにアクセスする必要がある要求だけが待機するようになりました。
「ディスクからの読み取り」操作は、ブロッキング操作の最も一般的な例としてよく使用されますが、実際には、NGINX のスレッド プールの実装は、メインの作業サイクルで処理するのが適切でない任意のタスクに使用できます。
現時点では、スレッド プールへのオフロードは、ほとんどのオペレーティング システムでのread()
システム コール、Linux でのsendfile()
、およびキャッシュ用などの一時ファイルを書き込むときに使用される Linux でのaio_write()
の 3 つの重要な操作に対してのみ実装されています。 私たちは実装のテストとベンチマークを継続し、明らかな利点がある場合は将来のリリースで他の操作をスレッド プールにオフロードする可能性があります。
エディター – aio_write()
システムコールのサポートは、 NGINX 1.9.13およびNGINX Plus R9で追加されました。
理論から実践に移る時が来ました。 スレッド プールの使用による効果を示すために、ブロッキング操作と非ブロッキング操作の最悪の組み合わせをシミュレートする合成ベンチマークを実行します。
メモリに収まらないことが保証されたデータ セットが必要です。 48 GB の RAM を搭載したマシンで、4 MB のファイルに 256 GB のランダム データを生成し、それを提供するように NGINX 1.9.0 を構成しました。
設定は非常にシンプルです:
ワーカープロセス 16;
イベント {
accept_mutex オフ;
}
http {
mime.types を含める;
default_type application/octet-stream;
access_log オフ;
sendfile オン;
sendfile_max_chunk 512k;
サーバー {
listen 8000;
場所 / {
root /storage;
}
}
}
ご覧のとおり、パフォーマンスを向上させるために、いくつかのチューニングが行われました。ログ記録
とaccept_mutex は
無効にされ、 sendfile
は有効にされ、 sendfile_max_chunk
が設定されました。 最後のディレクティブは、NGINX がファイル全体を一度に送信するのではなく、512 KB のチャンクで送信するため、 sendfile()
呼び出しのブロックに費やされる最大時間を短縮できます。
このマシンには、2 つの Intel Xeon E5645 (12 コア、合計 24 HT スレッド) プロセッサと 10 Gbps のネットワーク インターフェイスが搭載されています。 ディスク サブシステムは、RAID10 アレイに配置された 4 つの Western Digital WD1003FBYX ハード ドライブによって表されます。 このハードウェアはすべて、Ubuntu Server 14.04.1 LTS を搭載しています。
クライアントは、同じ仕様の 2 台のマシンで表されます。 これらのマシンの 1 つで、 wrk は
Lua スクリプトを使用して負荷を作成します。 このスクリプトは、200 個の並列接続を使用してランダムな順序でサーバーにファイルを要求します。各要求により、キャッシュ ミスが発生し、ディスクからの読み取りがブロックされる可能性があります。 この負荷をランダム負荷と呼びます。
2 番目のクライアント マシンでは、50 の並列接続を使用して同じファイルを複数回要求するwrk
の別のコピーを実行します。 このファイルは頻繁にアクセスされるため、常にメモリ内に残ります。 通常の状況では、NGINX はこれらのリクエストを非常に迅速に処理しますが、ワーカー プロセスが他のリクエストによってブロックされるとパフォーマンスが低下します。 この負荷を一定負荷と呼びます。
パフォーマンスは、 ifstat を
使用してサーバー マシンのスループットを監視し、2 番目のクライアントからwrk
結果を取得することによって測定されます。
さて、スレッド プールなしでの最初の実行では、あまり期待通りの結果は得られませんでした。
% ifstat -bi eth2 eth2 Kbps 入力 Kbps 出力 5531.24 1.03e+06 4855.23 812922.7 5994.66 1.07e+06 5476.27 981529.3 6353.62 1.12e+06 5166.17 892770.3 5522.81 978540.8 6208.10 985466.7 6370.79 1.12e+06 6123.33 1.07e+06
ご覧のとおり、この構成では、サーバーは合計で約 1 Gbps のトラフィックを生成できます。 top
からの出力では、すべてのワーカープロセスがほとんどの時間をブロッキング I/O に費やしていることがわかります ( D
状態です)。
トップ - 10:40:47 11 日間稼働、1:32、ユーザー 1 人、平均負荷: 49.61、45.77 62.89タスク:375合計、2ランニング、373眠っている、0停止した、0ゾンビ %Cpu(s):0.0私たち、0.3 sy、0.0に、67.7 id、31.9わ、0.0こんにちは、0.0シ、0.0 st KiB メモリ:49453440合計、49149308使用済み、304132無料、98780バッファ KiB スワップ:10474236合計、20124使用済み、10454112無料、46903412キャッシュされたメモリ PID ユーザー PR NI 仮想リソース SHR S %CPU %MEM 時間+ コマンド 4639 vbart 20 0 47180 28152 496 D 0.7 0.1 0:00.17 nginx 4632 vbart 20 0 47180 28196 536 D 0.3 0.1 0:00.11 nginx 4633 vbart 20 0 47180 28324 540 D 0.3 0.1 0:00.11 nginx 4635 vbart 20 0 47180 28136 480 D 0.3 0.1 0:00.12 nginx 4636 vbart 20 0 47180 28208 536 D 0.3 0.1 0:00.14 nginx 4637 vbart 20 0 47180 28208 536 D 0.3 0.1 0:00.10 nginx 4638 vbart 20 0 47180 28204 536 D 0.3 0.1 0:00.12 nginx 4640 vbart 20 0 47180 28324 540 D 0.3 0.1 0:00.13 nginx 4641 vbart 20 0 47180 28324 540 D 0.3 0.1 0:00.13 nginx 4642 vbart 20 0 47180 28208 536 D 0.3 0.1 0:00.11 nginx 4643 vbart 20 0 47180 28276 536 D 0.3 0.1 0:00.29 nginx 4644 vbart 20 0 47180 28204 536 D 0.3 0.1 0:00.11 nginx 4645 vbart 20 0 47180 28204 536 D 0.3 0.1 0:00.17 nginx 4646 vbart 20 0 47180 28204 536 D 0.3 0.1 0:00.12 nginx 4647 vbart 20 0 47180 28208 532 D 0.3 0.1 0:00.17 nginx 4631 vbart 20 0 47180 756 252 S 0.0 0.1 0:00.00 nginx 4634 vbart 20 0 47180 28208 536 D 0.0 0.1 0:00.11 nginx< 4648 vbart 20 0 25232 1956 1160 R 0.0 0.0 0:00.08 トップ25921 vbart 20 0 121956 2232 1056 S 0.0 0.0 0:01.97 sshd 25923 vbart 20 0 40304 4160 2208 S 0.0 0.0 0:00.53 zsh
この場合、スループットはディスク サブシステムによって制限され、CPU はほとんどの時間アイドル状態になります。 wrk
の結果も非常に低いです:
http://192.0.2.1:8000/1/1/1 で 1 分間のテストを実行中 12 スレッド、50 接続
スレッド統計 平均標準偏差 最大 +/- 標準偏差
レイテンシ 7.42 秒 5.31 秒 24.41 秒 74.73%
リクエスト/秒 0.15 0.36 1.00 84.62%
1.01 分間に 488 件のリクエスト、2.01 GB の読み取り
リクエスト/秒: 8.08
転送/秒: 34.07MB
そして、これはメモリから提供されるファイル用であることを忘れないでください。 レイテンシが過度に大きいのは、すべてのワーカー プロセスが、最初のクライアントからの 200 の接続によって生じるランダムな負荷に対応するためにドライブからファイルを読み取るのに忙しく、リクエストを適切なタイミングで処理できないためです。
スレッド プールを実際に使用してみましょう。 このためには、 location
ブロックにaio
threads
ディレクティブを追加するだけです。
場所 / { ルート /storage;
aio スレッド;
}
NGINX に設定を再読み込みするよう指示します。
その後、テストを繰り返します。
% ifstat -bi eth2 eth2 Kbps 入力 Kbps 出力 60915.19 9.51e+06 59978.89 9.51e+06 60122.38 9.51e+06 61179.06 9.51e+06 61798.40 9.51e+06 57072.97 9.50e+06 56072.61 9.51e+06 61279.63 9.51e+06 61243.54 9.51e+06 59632.50 9.50e+06
現在、当社のサーバーは、スレッド プールがない場合の約 1 Gbps と比較して、 9.5 Gbpsを生成します。
おそらくさらに多くのものを生成できるでしょうが、すでに実用的な最大ネットワーク容量に達しているため、このテストでは NGINX はネットワーク インターフェイスによって制限されます。 ワーカー プロセスは、ほとんどの時間をスリープ状態と新しいイベントの待機に費やします ( top
ではS
状態です)。
トップ - 10:43:17 11 日間稼働、1:35、ユーザー 1 人、負荷平均: 172.71、93.84、77.90タスク:376合計、1ランニング、375眠っている、0停止した、0ゾンビ %Cpu(s):0.2私たち、1.2 sy、0.0に、34.8 id、61.5わ、0.0こんにちは、2.3シ、0.0 st KiB メモリ:49453440合計、49096836使用済み、356604無料、97236バッファ KiB スワップ:10474236合計、22860使用済み、10451376無料、46836580キャッシュされたメモリ PID ユーザー PR NI 仮想リソース SHR S %CPU %MEM TIME+ コマンド 4654 vbart 20 0 309708 28844 596 S 9.0 0.1 0:08.65 nginx 4660 vbart 20 0 309748 28920 596 S 6.6 0.1 0:14.82 nginx 4658 vbart 20 0 309452 28424 520 S 4.3 0.1 0:01.40 nginx 4663 vbart 20 0 309452 28476 572 S 4.3 0.1 0:01.32 nginx 4667 vbart 20 0 309584 28712 588 S 3.7 0.1 0:05.19 nginx 4656 vbart 20 0 309452 28476 572 S 3.3 0.1 0:01.84 nginx 4664 vbart 20 0 309452 28428 524 S 3.3 0.1 0:01.29 nginx 4652 vbart 20 0 309452 28476 572 S 3.0 0.1 0:01.46 nginx 4662 vbart 20 0 309552 28700 596 S 2.7 0.1 0:05.92 nginx 4661 vbart 20 0 309464 28636 596 S 2.3 0.1 0:01.59 nginx 4653 vbart 20 0 309452 28476 572 S 1.7 0.1 0:01.70 nginx 4666 vbart 20 0 309452 28428 524 S 1.3 0.1 0:01.63 nginx 4657 vbart 20 0 309584 28696 592 S 1.0 0.1 0:00.64 nginx 4655 vbart 20 0 30958 28476 572 S 0.7 0.1 0:02.81 nginx 4659 vbart 20 0 309452 28468 564 S 0.3 0.1 0:01.20 nginx 4665 vbart 20 0 309452 28476 572 S 0.3 0.1 0:00.71 nginx 5180 vbart 20 0 25232 1952 1156 R 0.0 0.0 0:00.45 トップ4651 vbart 20 0 20032 752 252 S 0.0 0.0 0:00.00 nginx 25921 vbart 20 0 121956 2176 1000 S 0.0 0.0 0:01.98 sshd 25923 vbart 20 0 40304 3840 2208 S 0.0 0.0 0:00.54 zsh
CPU リソースはまだ十分にあります。
作業
の結果:
http://192.0.2.1:8000/1/1/1 で 12 スレッド、50 接続の 1 分間のテストを実行中
スレッド統計 平均標準偏差 最大 +/- 標準偏差
レイテンシ 226.32 ミリ秒 392.76 ミリ秒 1.72 秒 93.48%
リクエスト/秒 20.02 10.84 59.00 65.91%
1.00 分間に 15045 件のリクエスト、58.86 GB の読み取り
リクエスト/秒: 250.57
転送/秒: 0.98GB
4 MB のファイルを提供する平均時間は 7.42 秒から 226.32 ミリ秒 (33 分の 1) に短縮され、1 秒あたりのリクエスト数は 31 倍 (250 対 8) 増加しました。
説明としては、ワーカー プロセスが読み取りをブロックされている間、リクエストはイベント キューで処理を待機しなくなり、空きスレッドによって処理されるようになります。 ディスク サブシステムが最初のクライアント マシンからのランダムな負荷をできる限り処理している間、NGINX は残りの CPU リソースとネットワーク容量を使用して、メモリから 2 番目のクライアントの要求を処理します。
ブロッキング操作に関する懸念といくつかの興味深い結果を踏まえると、おそらく皆さんのほとんどはすでにサーバー上でスレッド プールを構成することになるでしょう。 急がないでください。
幸いなことに、ほとんどのファイルの読み取りおよび送信操作は、低速のハード ドライブを扱うものではありません。 データセットを保存するのに十分な RAM がある場合、オペレーティング システムは頻繁に使用されるファイルを「ページ キャッシュ」と呼ばれる場所にキャッシュする機能を備えています。
ページ キャッシュは非常にうまく機能し、NGINX はほぼすべての一般的なユース ケースで優れたパフォーマンスを発揮できます。 ページ キャッシュからの読み取りは非常に高速であり、このような操作を「ブロッキング」と呼ぶことはできません。 一方、スレッド プールへのオフロードには、ある程度のオーバーヘッドが発生します。
したがって、適切な量の RAM があり、作業データセットがそれほど大きくない場合は、NGINX はスレッドプールを使用せずに最適な方法で動作します。
読み取り操作をスレッド プールにオフロードすることは、非常に特定のタスクに適用できる手法です。 これは、頻繁に要求されるコンテンツの量がオペレーティング システムの VM キャッシュに収まらない場合に最も役立ちます。 これは、たとえば、負荷の高い NGINX ベースのストリーミング メディア サーバーの場合に当てはまる可能性があります。 これは、ベンチマークでシミュレートした状況です。
読み取り操作のスレッド プールへのオフロードを改善できれば素晴らしいと思います。 必要なのは、必要なファイル データがメモリ内にあるかどうかを効率的に知る方法だけです。後者の場合にのみ、読み取り操作を別のスレッドにオフロードする必要があります。
販売の例えに戻ると、現在、販売員は要求された商品が店内にあるかどうかを知ることができず、常にすべての注文を配送サービスに渡すか、常に自分で処理する必要があります。
原因は、オペレーティング システムにこの機能が欠けていることです。 これをfincore()
システムコールとして Linux に追加する最初の試みは 2010 年に行われましたが、実現しませんでした。 その後、 RWF_NONBLOCK
フラグを指定した新しいpreadv2()
システムコールとして実装する試みが何度か行われました (詳細については、LWN.net の「非ブロッキング バッファリング ファイル読み取り操作」および「非同期バッファリング読み取り操作」を参照してください)。 これらすべてのパッチの運命はまだ不明です。 ここで悲しいのは、これらのパッチがまだカーネルに受け入れられていない主な理由が、継続的なバイクシェディングであると思われることです。
一方、FreeBSD のユーザーはまったく心配する必要はありません。 FreeBSD には、ファイルを読み取るための十分に優れた非同期インターフェースがすでに備わっているので、スレッド プールの代わりにこれを使用する必要があります。
したがって、ユースケースでスレッド プールを使用することで何らかのメリットが得られると確信している場合は、構成を詳しく調べる必要があります。
設定は非常に簡単で柔軟です。 まず最初に、 configure
コマンドに--with-threads
引数を指定してコンパイルされた NGINX バージョン 1.7.11 以降が必要です。 NGINX Plus ユーザーにはリリース 7 以降が必要です。 最も単純なケースでは、構成は非常に単純に見えます。 必要なのは、適切なコンテキストにaio
threads
ディレクティブを含めることだけです。
# 'http'、'server'、または 'location' コンテキストのスレッド内;
これはスレッド プールの最小限の構成です。 実際、これは次の構成の短縮バージョンです。
# 'main' コンテキストの場合thread_pool default threads=32 max_queue=65536;
# 'http'、'server'、または 'location' コンテキストの場合
aio threads=default;
これは、32 個の作業スレッドと 65536 個のタスクのタスク キューの最大長を持つdefaultと呼ばれるスレッド プールを定義します。 タスク キューが過負荷になると、NGINX はリクエストを拒否し、次のエラーをログに記録します。
スレッド プール " NAME " キュー オーバーフロー: Nタスクが待機中
このエラーは、スレッドがキューに追加された作業をすぐに処理できない可能性があることを意味します。 最大キュー サイズを増やすこともできますが、それでも問題が解決しない場合は、システムがそれほど多くのリクエストを処理できないことを示しています。
すでにお気づきのとおり、 thread_pool
ディレクティブを使用すると、スレッドの数、キューの最大長、特定のスレッド プールの名前を設定できます。 最後は、複数の独立したスレッド プールを構成し、構成ファイルのさまざまな場所でそれらを使用することで、さまざまな目的に使用できることを意味します。
# 'main' コンテキスト内
thread_pool one threads=128 max_queue=0;
thread_pool two threads=32;
http {
server {
location /one {
aio threads=one;
}
location /two {
aio threads=two;
}
}
# ...
}
max_queue
パラメータが指定されていない場合は、デフォルトで値 65536 が使用されます。 示されているように、 max_queue を
ゼロに設定することも可能です。 この場合、スレッド プールは、構成されているスレッドの数と同じ数のタスクのみを処理でき、キューで待機するタスクはありません。
ここで、3 つのハード ドライブを備えたサーバーがあり、このサーバーをバックエンドからのすべての応答をキャッシュする「キャッシュ プロキシ」として機能させたいとします。 予想されるキャッシュデータの量が、使用可能な RAM を大幅に超えています。 これは実際には個人用 CDN のキャッシュ ノードです。 もちろん、この場合最も重要なことは、ドライブから最大のパフォーマンスを実現することです。
オプションの 1 つは、RAID アレイを構成することです。 このアプローチには長所と短所があります。 NGINX を使用すると、もう 1 つ次のことが可能になります。
# 各ハード ドライブが次のディレクトリのいずれかにマウントされていると想定します:# /mnt/disk1、/mnt/disk2、または /mnt/disk3
# 'main' コンテキスト内
thread_pool pool_1 threads=16;
thread_pool pool_2 threads=16;
thread_pool pool_3 threads=16;
http {
proxy_cache_path /mnt/disk1 levels=1:2 keys_zone=cache_1:256m max_size=1024G
use_temp_path=off;
proxy_cache_path /mnt/disk2 levels=1:2 keys_zone=cache_2:256m max_size=1024G
use_temp_path=off;
proxy_cache_path /mnt/disk3 levels=1:2 keys_zone=cache_3:256m max_size=1024G
use_temp_path=off;
split_clients $request_uri $disk {
33.3% 1;
33.3% 2;
* 3;
}
server {
# ...
location / {
proxy_pass http://backend;
proxy_cache_key $request_uri;
proxy_cache cache_$disk;
aio threads=pool_$disk;
sendfile on;
}
}
}
この構成では、 thread_pool
ディレクティブは各ディスクに専用の独立したスレッド プールを定義し、 proxy_cache_path
ディレクティブは各ディスクに専用の独立したキャッシュを定義します。
split_clients
モジュールは、キャッシュ間 (および結果としてディスク間) の負荷分散に使用され、このタスクに最適です。
proxy_cache_path
ディレクティブのuse_temp_path=off
パラメータは、対応するキャッシュ データが配置されているのと同じディレクトリに一時ファイルを保存するように NGINX に指示します。 キャッシュを更新するときに、ハードドライブ間で応答データがコピーされるのを避けるために必要です。
これらすべてを組み合わせることで、NGINX が個別のスレッド プールを介してドライブと並行して独立して対話するため、現在のディスク サブシステムから最大のパフォーマンスを引き出すことができます。 各ドライブは、ファイルの読み取りと送信専用のタスク キューを備えた 16 個の独立したスレッドによって処理されます。
きっとあなたのクライアントは、このカスタマイズされたアプローチを気に入ってくれるでしょう。 ハードドライブもこれを好むことを確認してください。
この例は、NGINX がハードウェアに合わせてどれだけ柔軟に調整できるかを示す良い例です。 これは、マシンやデータセットと対話する最適な方法について NGINX に指示を与えているようなものです。 また、ユーザー空間で NGINX を微調整することで、ソフトウェア、オペレーティング システム、ハードウェアが最適なモードで連携し、すべてのシステム リソースを可能な限り効率的に活用できるようになります。
まとめると、スレッド プールは、特に大量のコンテンツを扱う場合に、NGINX のよく知られた長年の敵の 1 つであるブロッキングを排除することで、NGINX のパフォーマンスを新たなレベルに押し上げる優れた機能です。
そして、さらにこれからさらに多くのことが起こります。 前述したように、この新しいインターフェースにより、パフォーマンスを損なうことなく、長時間の操作やブロッキング操作をオフロードできるようになります。 NGINX は、大量の新しいモジュールと機能を備えるという点で、新たな地平を開きます。 多くの人気ライブラリは、非同期の非ブロッキング インターフェイスをまだ提供していません。そのため、以前は NGINX と互換性がありませんでした。何らかのライブラリの独自の非ブロッキング プロトタイプの開発に多くの時間とリソースを費やすかもしれませんが、その努力は常に価値があるのでしょうか? 現在では、スレッド プールが搭載されているため、このようなライブラリを比較的簡単に使用でき、パフォーマンスに影響を与えずにこのようなモジュールを作成できます。
乞うご期待。
NGINX Plus のスレッド プールをぜひお試しください。今すぐ30 日間の無料トライアルを開始するか、弊社にお問い合わせの上、ユースケースについてご相談ください。
「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 以前の NGINX.com リンクはすべて、F5.com の同様の NGINX コンテンツにリダイレクトされます。"