nginx パニック問題の解決方法の詳細な説明

nginx パニック問題の解決方法の詳細な説明

nginx パニック問題に関しては、まず nginx の起動プロセス中に、マスター プロセスが構成ファイルで指定されたポートをリッスンし、次にマスター プロセスが fork() メソッドを呼び出して各子プロセスを作成することを理解する必要があります。プロセスの動作原理によれば、子プロセスは親プロセスのすべてのメモリ データとリッスン ポートを継承します。つまり、ワーカー プロセスも起動後に各ポートをリッスンすることになります。集団ショックとは、クライアントが新しい接続を要求すると、各ワーカー プロセスの接続確立イベントがトリガーされますが、そのイベントを正常に処理できるワーカー プロセスは 1 つだけであり、他のワーカー プロセスはイベントの有効期限が切れたことを検出し、待機状態に戻ることを意味します。イベントによってすべてのワーカー プロセスが「驚愕」するこの現象は、集団驚愕問題と呼ばれます。当然ですが、すべてのワーカー プロセスがトリガーされると、大量のリソースが消費されます。この記事では、主に nginx が herd 問題をどのように処理するかについて説明します。

1. 解決策

前回の記事では、各ワーカー プロセスが作成されると、現在のワーカー プロセスを初期化するために ngx_worker_process_init() メソッドが呼び出されることについて説明しました。このプロセスには非常に重要なステップがあります。つまり、各ワーカー プロセスは epoll_create() メソッドを呼び出して、独自の epoll ハンドルを作成します。リッスンする必要がある各ポートには、対応するファイル記述子があります。ワーカー プロセスは、epoll_ctl() メソッドを通じて現在のプロセスの epoll ハンドルにファイル記述子を追加し、accept イベントをリッスンするだけです。その後、クライアントの接続確立イベントによってトリガーされ、イベントを処理します。また、ワーカー プロセスが、リッスンするポートに対応するファイル記述子をプロセスの epoll ハンドルに追加しない場合、対応するイベントをトリガーできないこともここでわかります。この原則に基づいて、nginx は共有ロックを使用して、現在のプロセスが監視対象のポートを現在のプロセスの epoll ハンドルに追加する権限を持っているかどうかを制御します。つまり、ロックを取得したプロセスのみが対象ポートを監視します。この方法では、イベントが発生するたびに 1 つのワーカー プロセスのみがトリガーされることが保証されます。次の図は、ワーカー プロセスの動作サイクルの概略図を示しています。

図のプロセスについて説明しておく必要があるのは、各ワーカー プロセスがループに入った後に共有ロックを取得しようとすることです。ロックの取得に失敗した場合、監視対象ポートのファイル記述子は、現在のプロセスの epoll ハンドルから削除されます (存在しない場合でも)。これを行う主な目的は、クライアント接続イベントの損失を防ぐことです。これにより、多少のパニック問題が発生する可能性がありますが、深刻ではありません。理論上、現在のプロセスがロックを解放すると、監視対象ポートのファイル記述子が epoll ハンドルから削除されるとします。すると、次のワーカー プロセスがロックを取得するまで、この期間中、各ポートに対応するファイル記述子はどの epoll ハンドルでも監視されず、イベントが失われます。一方、図のようにロック取得に失敗した場合にのみ監視対象のファイル記述子を削除する場合は、ロック取得に失敗するということは、すでにこれらのファイル記述子をリッスンしているプロセスが存在するはずなので、この時点で削除しても安全です。ただし、これによって発生する問題の 1 つは、上の図によると、現在のプロセスがループを完了すると、ロックが解放されて他のイベントが処理されることです。このプロセス中は、監視対象のファイル記述子は解放されないことに注意してください。このとき、別のプロセスがロックを取得してファイル記述子をリッスンしている場合、ファイル記述子をリッスンしているプロセスが 2 つあることになります。そのため、クライアント側で接続確立イベントが発生すると、2 つのワーカー プロセスが起動されます。この問題は、主に次の 2 つの理由により許容可能です。

  1. このとき発生する集団ショック現象は、少数のワーカー プロセスのみをトリガーするため、毎回すべてのワーカー プロセスにショックを与えるよりもはるかに優れています。
  2. この集団パニック問題の主な原因は、現在のプロセスがロックを解放したが、監視対象のファイル記述子を解放しなかったことです。ロックを解放した後、ワーカープロセスは主にクライアント接続の読み取りおよび書き込みイベントを処理し、フラグをチェックします。このプロセスは非常に短く、処理後にロックを取得しようとし、監視対象のファイル記述子を解放します。比較すると、ロックを取得したワーカープロセスはクライアントの接続確立イベントを待つのに時間がかかるため、集団パニック問題が発生する可能性は比較的小さくなります。

2. ソースコードの説明

ワーカー プロセスのイベントを初期化する方法は、主に ngx_process_events_and_timers() メソッドで実行されます。このメソッドがプロセス全体をどのように処理するかを見てみましょう。以下はこのメソッドのソース コードです。

ngx_process_events_and_timers は ngx_cycle_t *cycle を呼び出します。
 ngx_uint_t フラグ;
 ngx_msec_t タイマー、デルタ;

 ngx_trylock_accept_mutex(cycle) == NGX_ERROR の場合 {
  戻る;
 }

 // ここでイベントの処理を開始します。kqueue モデルの場合、ngx_kqueue_process_events() メソッドを指します。
 // epoll モデルの場合、ngx_epoll_process_events() メソッドを指します // このメソッドの主な機能は、対応するイベント モデルのイベント リストを取得し、イベントを ngx_posted_accept_events に追加することです
 // キューまたは ngx_posted_events キュー (void) ngx_process_events(cycle, timer, flags);

 // ここで accept イベントの処理を開始し、ngx_event_accept.c の ngx_event_accept() メソッドに渡します。
 ngx_event_process_posted(サイクル、&ngx_posted_accept_events);

 // ロックの解放を開始する if (ngx_accept_mutex_held) {
  ngx_shmtx_unlock(&ngx_accept_mutex);
 }

 // イベント キューで処理する必要がない場合は、イベントを直接処理します // イベント処理では、accept イベントの場合は、ngx_event_accept.c の ngx_event_accept() メソッドに渡されて処理されます。
 // 読み取りイベントの場合は、ngx_http_request.c の ngx_http_wait_request_handler() メソッドによって処理されます。
 // 処理されたイベントは、最終的には ngx_http_request.c の ngx_http_keepalive_handler() メソッドによって処理されます。

 // イベント受け入れ以外の他のイベントの処理を開始します ngx_event_process_posted(cycle, &ngx_posted_events);
}

上記のコードでは、チェック作業のほとんどを省略し、スケルトンコードのみを残しました。まず、ワーカー プロセスは ngx_trylock_accept_mutex() メソッドを呼び出してロックを取得します。ロックが取得されると、各ポートに対応するファイル記述子をリッスンします。次に、 ngx_process_events() メソッドが呼び出され、 epoll ハンドルで監視されているイベントが処理されます。その後、共有ロックが解放され、最後に接続したクライアントの読み取りおよび書き込みイベントが処理されます。 ngx_trylock_accept_mutex() メソッドが共有ロックを取得する方法を見てみましょう。

ngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle) {
 // CASアルゴリズムを使用して共有ロックを取得しようとする if (ngx_shmtx_trylock(&ngx_accept_mutex)) {

  // ngx_accept_mutex_held は 1 で、現在のプロセスがロックを取得したことを示します if (ngx_accept_mutex_held && ngx_accept_events == 0) {
   NGX_OK を返します。
  }

  // ここでは、現在の接続のファイル記述子が、kqueue モデルの change_list 配列などの対応するイベントのキューに登録されます。 // nginx が各ワーカー プロセスを有効にすると、デフォルトでは、ワーカー プロセスはマスター プロセスによって監視されるソケット ハンドルを継承します。
  // これにより問題が発生します。つまり、ポートにクライアント イベントが発生すると、そのポートをリッスンしているすべてのプロセスが起動されます。
  // ただし、イベントを正常に処理できるワーカー プロセスは 1 つだけであり、他のプロセスは起動後にイベントの有効期限が切れていることを検出します。
  // そのため、待機状態に入り続けることになります。これを「群集ショック」現象と呼びます。
  // nginx が集団パニック現象を解決する方法は、ここでは共有ロック方式です。つまり、ロックを取得したワーカー プロセスのみがクライアント イベントを処理できますが、実際には、ワーカー プロセスはロックを取得するプロセスで、現在のワーカー プロセスの各ポートのリスニング イベントを再追加します。
  // 他のワーカープロセスは監視しません。つまり、各ポートを同時にリッスンするのは 1 つのワーカー プロセスだけです。
  // これにより、「集団ショック」の問題を回避できます。
  // ここでの ngx_enable_accept_events() メソッドは、現在のプロセスの各ポートのリスニング イベントを再度追加します。
  ngx_enable_accept_events(cycle) == NGX_ERROR の場合 {
   ngx_shmtx_unlock(&ngx_accept_mutex);
   NGX_ERROR を返します。
  }

  // フラグはロックが正常に取得されたことを示します ngx_accept_events = 0;
  ngx_accept_mutex_held = 1;

  NGX_OK を返します。
 }

 // 以前にロックの取得に失敗しました。そのため、ngx_accept_mutex_held の状態をリセットし、現在の接続のイベントをクリアする必要があります。if (ngx_accept_mutex_held) {
  // 現在のプロセスの ngx_accept_mutex_held が 1 の場合、それを 0 にリセットし、各ポートの現在のプロセスの監視イベントを削除します if (ngx_disable_accept_events(cycle, 0) == NGX_ERROR) {
   NGX_ERROR を返します。
  }

  ngx_accept_mutex_held = 0;
 }

 NGX_OK を返します。
}

上記のコードでは、基本的に次の 3 つの処理が行われます。

  1. CAS メソッドを使用して、ngx_shmtx_trylock() メソッドを通じて共有ロックを取得してみてください。
  2. ロックを取得した後、ngx_enable_accept_events() メソッドが呼び出され、ターゲット ポートに対応するファイル記述子をリッスンします。
  3. ロックが取得されない場合は、ngx_disable_accept_events() メソッドを呼び出して、監視対象のファイル記述子を解放します。

3. まとめ

この記事では、まず集団パニック現象の原因を説明し、次に nginx が集団パニック問題を解決する方法を紹介し、最後にソース コードの観点から nginx が集団パニック問題を処理する方法を説明します。

以上がこの記事の全内容です。皆様の勉強のお役に立てれば幸いです。また、123WORDPRESS.COM を応援していただければ幸いです。

<<:  計算機機能を実装するミニプログラム

>>:  MySQL インデックスがソートに与える影響の分析例

推薦する

ウェブサイトのコンテンツが検索エンジンに含まれないようにする方法

通常、Web サイトを構築する目的は、検索エンジンにインデックス登録してもらい、プロモーションを拡大...

Dockerイメージの作成、アップロード、プル、デプロイを理解するための記事

目次1. 画像1. 鏡とは何ですか? 2. 画像の構成と目的(1) Dockerファイル(2)スクラ...

Redis イメージの Docker インストールと設定手順

目次序文環境インストールMySQLコンテナを作成して起動する落とし穴を避けるための注意MySQLコン...

WeChatアプレットは固定ヘッダーとリストテーブルコンポーネントを実装します

目次必要:機能ポイントレンダリング実装のアイデア具体的なコード(react\taro3.0)特定のコ...

Mysql | ワイルドカード(%、_ など)を使用したファジークエリの詳細な説明

ワイルドカードのカテゴリ: %パーセント ワイルドカード: 任意の文字が任意の回数出現できることを示...

MySQL <> および <=> 演算子の紹介

<> 演算子機能: 等しくないことを示します。注: 「!=」演算子と同じ機能を持ちますが...

背景のグラデーションと自動フルスクリーンを実現するCSSコード

背景グラデーションと自動フルスクリーンに関する CSS の問題編集長は CSS の開発中に致命的な問...

HTML タグのカスタム属性に関する質問

以前の開発では、クラス、名前などの HTML のデフォルト属性を使用していました。 Huawei社の...

Tomcat でのコネクタ構成

JBoss は Tomcat を Web コンテナとして使用するため、JBoss の Web コンテ...

Docker コンテナの正常なシャットダウン前にトラップを使用して環境のクリーンアップを実行する

実行中のコンテナが終了したときに、コンテナが完全に終了する前に環境をクリーンアップするなど、いくつか...

表には表示したい境界コードが表示されます

テーブルの共通プロパティ基本的な属性は、width (幅)、height (高さ)、border (...

Linux のソフトリンクとハードリンクの詳細な説明

目次1. ファイルとディレクトリの基本的な保存2. Inコマンドの紹介(1)lnコマンドの基本情報を...

Nginx における 2 つの現在の制限方法についての簡単な説明

負荷は通常、システム設計時に予測されます。システムがパブリック ネットワークに公開されている場合、悪...

jsフェッチ非同期リクエストの使用の詳細な例

目次非同期を理解するフェッチ(url)レスポンス.json() asyncとawaitを組み合わせる...

vue プロジェクトのデプロイと Nginx でのプロキシ設定の問題の分析

1. nginxをインストールして起動する # nginxをインストールする sudo apt-ge...