nginx を開始すると、Unix システムのデーモンとしてバックグラウンドで実行され、バックグラウンド プロセスにはマスター プロセスと複数のワーカー プロセスが含まれます。バックグラウンド モードを手動でオフにして、nginx をフォアグラウンドで実行させ、マスター プロセスをキャンセルするように nginx を構成して、nginx をシングルプロセス モードで実行できるようにすることもできます。
明らかに、運用環境ではこれを実行しないため、バックグラウンド モードをオフにすることは通常、デバッグに使用されます。次の章では、nginx のデバッグ方法について詳しく説明します。
つまり、nginx がマルチプロセス方式で動作することがわかります。もちろん、nginx はマルチスレッドもサポートしています。ただし、私たちの主流の方法は依然としてマルチプロセス方式であり、これがデフォルトの方式でもありますnginxの。 nginxでマルチプロセスを使用すると多くのメリットがあるので、nginxのマルチプロセスモードを中心に説明します。
先ほども述べたように、nginx を起動するとマスタープロセスと複数のワーカープロセスが存在します。マスター プロセスは主にワーカー プロセスの管理に使用されます。これには、外部からのシグナルの受信、各ワーカー プロセスへのシグナルの送信、ワーカー プロセスの実行ステータスの監視、ワーカー プロセスの終了時に新しいワーカー プロセスを自動的に再起動するなどが含まれます。異常事態)です。
基本的なネットワーク イベントはワーカー プロセスで処理されます。複数のワーカー プロセスはピアツーピアであり、クライアントからのリクエストを平等に競合し、各プロセスは互いに独立しています。リクエストは 1 つのワーカー プロセスでのみ処理でき、ワーカー プロセスは他のプロセスからのリクエストを処理できません。ワーカープロセスの数は設定可能ですが、通常はマシンのCPUコア数に合わせて設定しますが、その理由はnginxのプロセスモデルやイベント処理モデルと切り離すことができません。
nginx が起動した後、nginx を操作したい場合はどうすればよいでしょうか?
上記のことから、マスターがワーカー プロセスを管理していることがわかります。そのため、マスター プロセスと通信するだけで済みます。マスター プロセスは外部から信号を受信し、その信号に基づいてさまざまな処理を実行します。したがって、nginx を制御したい場合は、kill を通じてマスタープロセスにシグナルを送信するだけで済みます。たとえば、kill -HUP pid は nginx に nginx を正常に再起動するように指示します。通常、この信号は nginx を再起動するか、設定をリロードするために使用されます。正常に再起動されるため、サービスは中断されません。 HUP 信号を受信した後、マスター プロセスは何をしますか?
まず、シグナルを受信した後、マスター プロセスは構成ファイルを再読み込みし、新しいワーカー プロセスを開始し、すべての古いワーカー プロセスにシグナルを送信して、正常にリタイアできることを伝えます。
新しいワーカーは開始後、新しいリクエストの受信を開始しますが、古いワーカーはマスターからのシグナルを受信した後、現在のプロセス内のすべての未処理のリクエストの受信を停止します。完全なリクエストの処理が完了した後、 出口。
もちろん、マスター プロセスにシグナルを直接送信するのは古い操作方法であり、nginx バージョン 0.8 以降では、管理を容易にするために一連のコマンド ライン パラメーターが導入されました。たとえば、./nginx -s reload は nginx を再起動し、./nginx -s stop は nginx の実行を停止します。 #########どうやってするの?
reload を例に見てみましょう。コマンドを実行すると、新しい nginx プロセスが開始され、新しい nginx プロセスは reload パラメータを解析した後に目的を認識します。 nginx を使用して構成ファイルをリロードすると、マスター プロセスにシグナルが送信され、次のアクションはマスター プロセスにシグナルを直接送信した場合と同じになります。
これで、nginx を操作するときに nginx が内部で何を行うかがわかりました。では、ワーカー プロセスはリクエストをどのように処理するのでしょうか?前述したように、ワーカー プロセスは平等であり、各プロセスにはリクエストを処理する同じ機会があります。ポート 80 で http サービスを提供し、接続要求が来た場合、各プロセスが接続を処理する場合があります。 まず、マスタープロセスから各ワーカープロセスがforkされ、マスタープロセスではlistenする必要のあるソケット(listenfd)が確立され、その後複数のワーカープロセスがforkされます。新しい接続が到着すると、すべてのワーカー プロセスの listenfd が読み取り可能になります。1 つのプロセスだけが接続を処理できるようにするため、すべてのワーカー プロセスは、listenfd 読み取りイベントを登録する前に、accept_mutex を取得します。mutex を取得するプロセスは、listenfd 読み取りイベントを登録します。接続を受け入れるには、読み取りイベントで accept を使用します。 ワーカー プロセスは接続を受け入れた後、リクエストの読み取り、リクエストの解析、処理、データの生成を開始し、それをクライアントに返し、最後に接続を切断します。それです。リクエストはワーカー プロセスによって完全に処理され、1 つのワーカー プロセスでのみ処理されることがわかります。マルチスレッド モデル VS マルチプロセス モデル、これは問題です。
それでは、nginx がこのプロセス モデルを採用するメリットは何でしょうか?もちろん、多くのメリットがあることは間違いありません。まず、ワーカープロセスごとに独立したプロセスとなり、ロックする必要がないため、ロックによるオーバーヘッドがなくなり、同時にプログラミング時や問題発見時に非常に便利になります。第 2 に、独立したプロセスを使用すると、相互に影響を与えることはありません。1 つのプロセスが終了した後も、他のプロセスはまだ動作しており、サービスは中断されません。マスター プロセスはすぐに新しいワーカー プロセスを開始します。もちろんワーカープロセスが異常終了した場合はプログラムにバグがあるはずで、異常終了すると現在のワーカー上のリクエストはすべて失敗しますが、すべてのリクエストに影響するわけではないのでリスクは軽減されます。もちろんメリットもたくさんあり、誰でもゆっくりと体験することができます。
上記では nginx のプロセス モデルについて詳しく説明しましたが、次に nginx がイベントをどのように処理するかを見てみましょう。
誰かが尋ねるかもしれませんが、nginx はリクエストを処理するためにマルチワーカー メソッドを使用します。各ワーカーにはメイン スレッドが 1 つしかないため、処理できる同時実行数は非常に限られています。ワーカーの数は何人として処理できますか?多くの同時実行性?、高い同時実行性を実現するにはどうすればよいですか?いいえ、これが nginx の優れた点です。nginx は、非同期かつノンブロッキングのメソッドを使用してリクエストを処理します。言い換えれば、nginx は同時に数千のリクエストを処理できます。
Apache の一般的な動作方法について考えてみましょう (Apache には非同期のノンブロッキング バージョンもありますが、独自のモジュールの一部と競合するため、一般的には使用されません)。作業スレッド。同時実行数が数千に達すると、数千のスレッドが同時にリクエストを処理します。これはオペレーティング システムにとって大きな課題です。スレッドが占有するメモリは非常に大きく、スレッド コンテキストの切り替えによって発生する CPU オーバーヘッドも非常に大きくなります。当然、パフォーマンスは向上せず、これらのオーバーヘッドはまったく意味がありません。
同期ブロッキング VS 非同期ノンブロッキング
なぜ nginx は非同期ノンブロッキングで処理できるのでしょうか?また、非同期ノンブロッキングとは正確には何ですか?出発点に戻って、リクエストの完全なプロセスを見てみましょう。まずリクエストが来てコネクションを確立してデータを受信し、データを受信した後にデータを送信します。システムの最下位層に特有のものは、読み取りイベントと書き込みイベントです。読み取りイベントと書き込みイベントの準備ができていない場合、必然的に操作不能になります。非ブロッキングな方法で呼び出さない場合は、次のことを行う必要があります。通話をブロックします。イベントの準備ができていない場合は、待つことしかできません。イベントの準備ができたら続行できます。呼び出しをブロックするとカーネルに入って待機し、CPU が他のユーザーによって使用されることになります。シングルスレッドのワーカーには明らかに適していません。ネットワーク イベントが増えると全員が待機し、CPU が使用されているときは誰も CPU を使用しません。 CPU 使用率 当然のことながら、高い同時実行性はおろか、レートを上げることもできません。
わかりました、プロセスの数を追加すると言いましたが、これと Apache のスレッド モデルの違いは何ですか? 不必要なコンテキスト スイッチングを増やさないように注意してください。したがって、nginxではシステムコールのブロックは最もタブーとされています。ブロックしない場合は、ノンブロッキングになります。ノンブロッキングとは、イベントの準備ができていない場合に、すぐに EAGAIN に戻り、イベントの準備がまだできていないことを通知することを意味します。なぜパニックになっているのですか? 後で戻ってください。しばらくしてから、イベントの準備ができるまでもう一度イベントを確認してください。この間、他の作業を先に行ってから、イベントの準備ができているかどうかを確認できます。ブロックされなくなりましたが、イベントのステータスを時々確認する必要があり、できることは増えましたが、オーバーヘッドは小さくありません。したがって、非同期のノンブロッキング イベント処理メカニズムがあり、特定のシステム コールは select/poll/epoll/kqueue のようなシステム コールです。
これらは、複数のイベントを同時に監視できるメカニズムを提供します。呼び出しはブロックされますが、タイムアウトを設定できます。タイムアウト内で、イベントの準備ができていれば、イベントが返されます。このメカニズムは、上記の 2 つの問題を解決するだけです。例として epoll を取り上げます (次の例では、このタイプの関数を表す例として epoll をよく使用します)。イベントの準備ができていない場合、イベントは epoll に配置されます。イベントの準備ができたら、読み取りと書き込みを実行し、読み取りと書き込みで EAGAIN が返されると、再度 epoll に追加します。このようにして、イベントの準備ができている限りそれを処理し、すべてのイベントの準備ができていない場合にのみ、epoll で待機します。このようにして、多数の同時リクエストを処理できます。もちろん、ここでの同時リクエストとは、未処理のリクエストを指します。スレッドは 1 つしかないため、同時に処理できるリクエストは 1 つだけです。非同期イベントの準備ができていないため、切り替えは自発的に中止されました。ここでの切り替えにはコストはかからず、準備された複数のイベントをループで処理するものとして理解できますが、実際にそうなっています。
マルチスレッドと比較すると、このイベント処理方法には、スレッドを作成する必要がなく、各リクエストが占有するメモリが非常に少なく、コンテキストの切り替えがなく、イベント処理が非常に軽量であるという大きな利点があります。同時実行がどれほど多くても、不必要なリソースの浪費 (コンテキストの切り替え) が発生することはありません。同時実行を増やすと、より多くのメモリを消費するだけです。以前に接続数をテストしたところ、24G メモリを搭載したマシンで同時に処理されたリクエストの数は 200 万を超えました。現在のネットワークサーバーは基本的にこの方式を採用しており、これがnginxのパフォーマンスが高い主な理由でもあります。
以前、ワーカー数を CPU コアの数に設定することをお勧めすると述べましたが、これは簡単に理解できます。ワーカー数が増えると、プロセスが CPU リソースをめぐって競合するだけになり、不必要なコンテキスト スイッチが発生します。
また、nginx ではマルチコア機能を有効に活用するために、CPU アフィニティ バインド オプションを提供しており、特定のプロセスを特定のコアにバインドすることができるため、プロセスの切り替えによる問題が発生しません。キャッシュ障害が発生します。このような小さな最適化は nginx では非常に一般的であり、これは nginx 作成者の骨の折れる努力を示しています。たとえば、nginxは4バイト文字列を比較する場合、CPUの命令数などを減らすために4文字をint型に変換して比較します。
これで、nginx がそのようなプロセス モデルとイベント モデルを選択する理由がわかりました。基本的な Web サーバーの場合、通常、ネットワーク イベント、シグナル、タイマーの 3 種類のイベントがあります。上記の説明から、ネットワーク イベントは非同期ノンブロッキングによってうまく解決できることがわかります。信号とタイマーをどのように扱うか?
まずは信号処理です。
nginx の場合、特定の意味を表す特定のシグナルがいくつかあります。このシグナルはプログラムの現在の実行を中断し、状態を変更した後も実行を継続します。システムコールの場合、システムコールが失敗し、再入力が必要になる可能性があります。信号処理については専門書で勉強できるので、ここでは詳しく説明しません。 nginx の場合、nginx がイベントを待機している場合 (epoll_wait 中に)、信号処理関数の処理後にプログラムが信号を受信すると、epoll_wait はエラーを返し、プログラムは再び epoll_wait 呼び出しに入ることができます。
さらに、タイマーについても見てみましょう。 epoll_wait およびその他の関数は呼び出されたときにタイムアウトを設定できるため、nginx はこのタイムアウトを使用してタイマーを実装します。 nginx のタイマー イベントは、タイマーを維持する赤黒ツリーに配置されます。epoll_wait に入る前に、すべてのタイマー イベントの最小時間が赤黒ツリーから取得され、epoll_wait のタイムアウトが計算されます。時間。
したがって、イベントが生成されず、割り込み信号もない場合、epoll_wait はタイムアウトになります。つまり、タイマー イベントが到着しました。この時点で、nginx はすべてのタイムアウト イベントをチェックし、ステータスをタイムアウトに設定して、ネットワーク イベントを処理します。このことから、nginx コードを作成するときに、ネットワーク イベントのコールバック関数を処理するときに通常最初に行うことは、タイムアウトを決定してからネットワーク イベントを処理することであることがわかります。
Nginx 関連の知識については、Nginx の使用方法チュートリアル 列をご覧ください。
以上がなぜnginxのパフォーマンスが良いのでしょうか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。