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 インデックスがソートに与える影響の分析例

推薦する

CSSマスクのフルスクリーン中央揃えを実装する方法

具体的なコードは次のとおりです。 <スタイル> #トーストローダーフルスクリーン{ 高さ...

WeChatアプレット開発によりホームページポップアップボックスアクティビティガイダンス機能が実現

目次1. 需要2. データベース設計3.Javaバックグラウンド構成の実装4. WeChatアプレッ...

CSS スタイルの導入方法とその長所と短所の紹介

CSSを導入する3つの方法1. インラインスタイル利点: 書きやすく、重みがある 欠点: 構造とスタ...

IDEA が Docker を統合してリモート展開を実現するための手順

1. Dockerサーバーへのリモートアクセスを有効にするdocker が配置されているリモート サ...

ウェブデザイナーが持つべき資質と能力

Web デザインは、インターネットの出現後に誕生した新興の周辺産業です。 Web ページは店頭のよう...

vue v-for ループ オブジェクトの属性

目次1. ループオブジェクト内の値2. ループオブジェクト3. キーと値のループ1. ループオブジェ...

ウェブサイトデザインにおいて非常に重要な概念であるdiv+floatの分析

ウェブサイトの構築では、HTML と CSS に関するさまざまな問題に常に遭遇します。ウェブサイト ...

JavaScript - Vue でのスロットの使用: スロット

目次Vue でのスロットの使用: slotスコープ付きスロット: テンプレートタグで囲む要約するVu...

IE10以下のimgタグ問題の解決方法

問題を見つける以前、簡単なデモを書いたのですが、IE10以下では動作しないことがわかりました。ここに...

JavaでTomcatサーバーを起動/停止する方法

1. プロジェクト構造 2.Tomcat.javaを呼び出す パッケージ com.calltomca...

Dockerサーバーのストレージリソースプール不足問題の解決

目次1. 問題の説明2. 問題分析3. 問題解決1. Dockerのディスク使用量を確認する2. 再...

LinuxサーバーにVueプロジェクトをデプロイする

ケース1 vue-cliはvue3プロジェクトをビルドし、プロジェクトをLinuxサーバーにアップロ...

win10 での mysql5.7.21 の詳細なインストール手順

この記事では、MySQL 5.7.21のインストールとインストール中に発生した問題を参考までに紹介し...

Dockerでプロジェクトを実行する方法

1. プロジェクトwarが保存されているディレクトリを入力しますDockerfileを編集する vi...

Linux で unzip コマンドを使用して複数のファイルを解凍する方法

Linuxにunzipコマンドがない問題の解決策unzipコマンドを使用して.zipファイルを解凍す...