ホームページ 運用・保守 Nginx Nginxのイベントドリブンフレームワークの処理フローはどのようなものですか?

Nginxのイベントドリブンフレームワークの処理フローはどのようなものですか?

May 13, 2023 pm 01:34 PM
nginx

ngx_event_core_module モジュールの ngx_event_process_init メソッドは、イベント モジュールの初期化を行います。これには、「接続要求」などの読み取りイベントに対応するハンドラーを ngx_event_accept 関数に設定し、このイベントを epoll モジュールに追加することが含まれます。新しい接続イベントが発生すると、ngx_event_accept が呼び出されます。

ワーカー プロセスは、イベントを処理するために ngx_worker_process_cycle メソッドの ngx_process_events_and_timers 関数を継続的に呼び出します。この関数は、イベント処理の一般的なエントリ ポイントです。

ngx_process_events_and_timers は、ngx_event_actions.process_events に相当するマクロである ngx_process_events を呼び出します。ngx_event_actions は、イベント駆動型モジュール (ここでは epoll モジュール) に対応する 10 個の関数インターフェイスを格納するグローバル構造です。したがって、ここでは ngx_epoll_module_ctx.actions.process_events 関数が呼び出されます。これは、イベントを処理するための ngx_epoll_process_events 関数です。

ngx_epoll_process_events は、Linux 関数インターフェイス epoll_wait を呼び出して「新しい接続」イベントを取得し、このイベントのハンドラー処理関数を呼び出してこのイベントを処理します。

上記の通り、ハンドラには ngx_event_accept 関数が設定されているため、実際の処理では ngx_event_accept が呼び出されます。

ngx_event_accept メソッドを分析したフローチャートは次のとおりです: Nginxのイベントドリブンフレームワークの処理フローはどのようなものですか?

簡略化したコードは次のとおりです コメント内のシリアル番号は上図のシリアル番号:

void
ngx_event_accept(ngx_event_t *ev)
{
 socklen_t  socklen;
 ngx_err_t  err;
 ngx_log_t  *log;
 ngx_uint_t  level;
 ngx_socket_t  s;
 ngx_event_t  *rev, *wev;
 ngx_listening_t  *ls;
 ngx_connection_t *c, *lc;
 ngx_event_conf_t *ecf;
 u_char  sa[ngx_sockaddrlen];
 
 if (ev->timedout) {
  if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != ngx_ok) {
   return;
  }
 
  ev->timedout = 0;
 }
 
 ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
 
 if (ngx_event_flags & ngx_use_rtsig_event) {
  ev->available = 1;
 
 } else if (!(ngx_event_flags & ngx_use_kqueue_event)) {
  ev->available = ecf->multi_accept;
 }
 
 lc = ev->data;
 ls = lc->listening;
 ev->ready = 0;
 
 do {
  socklen = ngx_sockaddrlen;
 
  /* 1、accept方法试图建立连接,非阻塞调用 */
  s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
 
  if (s == (ngx_socket_t) -1)
  {
   err = ngx_socket_errno;
 
   if (err == ngx_eagain)
   {
    /* 没有连接,直接返回 */
    return;
   }
 
   level = ngx_log_alert;
 
   if (err == ngx_econnaborted) {
    level = ngx_log_err;
 
   } else if (err == ngx_emfile || err == ngx_enfile) {
    level = ngx_log_crit;
   }
 
   if (err == ngx_econnaborted) {
    if (ngx_event_flags & ngx_use_kqueue_event) {
     ev->available--;
    }
 
    if (ev->available) {
     continue;
    }
   }
 
   if (err == ngx_emfile || err == ngx_enfile) {
    if (ngx_disable_accept_events((ngx_cycle_t *) ngx_cycle)
     != ngx_ok)
    {
     return;
    }
 
    if (ngx_use_accept_mutex) {
     if (ngx_accept_mutex_held) {
      ngx_shmtx_unlock(&ngx_accept_mutex);
      ngx_accept_mutex_held = 0;
     }
 
     ngx_accept_disabled = 1;
 
    } else {
     ngx_add_timer(ev, ecf->accept_mutex_delay);
    }
   }
 
   return;
  }
 
  /* 2、设置负载均衡阈值 */
  ngx_accept_disabled = ngx_cycle->connection_n / 8
        - ngx_cycle->free_connection_n;
 
  /* 3、从连接池获得一个连接对象 */
  c = ngx_get_connection(s, ev->log);
 
  /* 4、为连接创建内存池 */
  c->pool = ngx_create_pool(ls->pool_size, ev->log);
 
  c->sockaddr = ngx_palloc(c->pool, socklen);
 
  ngx_memcpy(c->sockaddr, sa, socklen);
 
  log = ngx_palloc(c->pool, sizeof(ngx_log_t));
 
  /* set a blocking mode for aio and non-blocking mode for others */
  /* 5、设置套接字属性为阻塞或非阻塞 */
  if (ngx_inherited_nonblocking) {
   if (ngx_event_flags & ngx_use_aio_event) {
    if (ngx_blocking(s) == -1) {
     ngx_log_error(ngx_log_alert, ev->log, ngx_socket_errno,
         ngx_blocking_n " failed");
     ngx_close_accepted_connection(c);
     return;
    }
   }
 
  } else {
   if (!(ngx_event_flags & (ngx_use_aio_event|ngx_use_rtsig_event))) {
    if (ngx_nonblocking(s) == -1) {
     ngx_log_error(ngx_log_alert, ev->log, ngx_socket_errno,
         ngx_nonblocking_n " failed");
     ngx_close_accepted_connection(c);
     return;
    }
   }
  }
 
  *log = ls->log;
 
  c->recv = ngx_recv;
  c->send = ngx_send;
  c->recv_chain = ngx_recv_chain;
  c->send_chain = ngx_send_chain;
 
  c->log = log;
  c->pool->log = log;
 
  c->socklen = socklen;
  c->listening = ls;
  c->local_sockaddr = ls->sockaddr;
  c->local_socklen = ls->socklen;
 
  c->unexpected_eof = 1;
 
  rev = c->read;
  wev = c->write;
 
  wev->ready = 1;
 
  if (ngx_event_flags & (ngx_use_aio_event|ngx_use_rtsig_event)) {
   /* rtsig, aio, iocp */
   rev->ready = 1;
  }
 
  if (ev->deferred_accept) {
   rev->ready = 1;
 
  }
 
  rev->log = log;
  wev->log = log;
 
  /*
   * todo: mt: - ngx_atomic_fetch_add()
   *  or protection by critical section or light mutex
   *
   * todo: mp: - allocated in a shared memory
   *   - ngx_atomic_fetch_add()
   *  or protection by critical section or light mutex
   */
 
  c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
 
  if (ls->addr_ntop) {
   c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);
   if (c->addr_text.data == null) {
    ngx_close_accepted_connection(c);
    return;
   }
 
   c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,
            c->addr_text.data,
            ls->addr_text_max_len, 0);
   if (c->addr_text.len == 0) {
    ngx_close_accepted_connection(c);
    return;
   }
  }
 
  /* 6、将新连接对应的读写事件添加到epoll对象中 */
  if (ngx_add_conn && (ngx_event_flags & ngx_use_epoll_event) == 0) {
   if (ngx_add_conn(c) == ngx_error) {
    ngx_close_accepted_connection(c);
    return;
   }
  }
 
  log->data = null;
  log->handler = null;
 
  /* 7、tcp建立成功调用的方法,这个方法在ngx_listening_t结构体中 */
  ls->handler(c);
 
 } while (ev->available); /* available标志表示一次尽可能多的建立连接,由配置项multi_accept决定 */
}
ログイン後にコピー

nginx

nginx の「ショック グループ」問題は通常、複数のワーカー プロセスを実行し、これらのプロセスは同じポートをリッスンします。同時に。新しい接続が到着すると、カーネルはこれらすべてのプロセスを起動しますが、クライアントに正常に接続できるのは 1 つのプロセスだけであり、起動時に他のプロセスが大量のオーバーヘッドを無駄にします。これは「サンダー ハード」現象と呼ばれます。 nginx が「ショック」問題を解決する方法は、プロセスにミューテックス ロック ngx_accept_mutex を取得させ、プロセスが特定のクリティカル セクションに相互に入るようにすることです。クリティカル セクションでは、プロセスは監視する接続に対応する読み取りイベントを epoll モジュールに追加します。これにより、「新しい接続」イベントが発生したときにワーカー プロセスが応答します。イベントをロックして追加するこのプロセスは、関数 ngx_trylock_accept_mutex で完了します。他のプロセスもこの関数に入り、読み取りイベントを追加したい場合、ミューテックスが別のプロセスによって保持されていることがわかるため、戻ることしかできず、リッスンしているイベントを epoll モジュールに追加できないため、応答できません。 「新しいつながり」イベント。しかし、これには疑問が生じます。ミューテックス ロックを保持しているプロセスはいつミューテックス ロックを解放するのでしょうか?ロックを解放する前にすべてのイベントが処理されるのを待つ必要がある場合、長い時間がかかります。この期間中、他のワーカー プロセスは新しい接続を確立できませんが、これは明らかに望ましくないことです。 nginx の解決策は次のとおりです。ngx_trylock_accept_mutex を通じてミューテックス ロックを取得したプロセスは、ready read/write イベントを取得して epoll_wait から戻った後、これらのイベントをキューに入れます。

新しい接続イベントは ngx_posted_accept_events に入れられます。 queue

既存の接続イベントを ngx_posted_events キューに入れます

コードは次のとおりです:

if (flags & ngx_post_events)
{
 /* 延后处理这批事件 */
 queue = (ngx_event_t **) (rev->accept ? &ngx_posted_accept_events : &ngx_posted_events);
 
 /* 将事件添加到延后执行队列中 */
 ngx_locked_post_event(rev, queue);
}
else
{
 rev->handler(rev); /* 不需要延后,则立即处理事件 */
}
ログイン後にコピー

同様の処理のイベントを書き込みます。次に、プロセスは ngx_posted_accept_events キュー内のイベントを処理し、プロセスがロックを占有する時間を最小限に抑えるために、処理後にすぐにミューテックス ロックを解放します。

nginx の負荷分散の問題

nginx の各プロセスは、負荷分散を処理するためにしきい値 ngx_accept_disabled を使用します。これは、上図の手順 2 で初期化されます。

ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n;

初期値は負の数であり、負の数の絶対値は合計の 7/8 に等しくなります。接続数。しきい値が 0 より小さい場合、新しい接続イベントに通常どおり応答します。しきい値が 0 より大きい場合、新しい接続イベントに応答しなくなり、ngx_accept_disabled が 1 ずつ減ります。コードは次のとおりです。

if (ngx_accept_disabled > 0)
{
  ngx_accept_disabled--;
}
else
{
 if (ngx_trylock_accept_mutex(cycle) == ngx_error)
 {
  return;
 }
 ....
}
ログイン後にコピー

これは、プロセスの現在の接続数が処理可能な総接続数の 7/8 に達すると、負荷分散メカニズムがトリガーされ、プロセスが新しい接続への応答を停止することを示しています。 ###

以上がNginxのイベントドリブンフレームワークの処理フローはどのようなものですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Dockerコンテナの名前を確認する方法 Dockerコンテナの名前を確認する方法 Apr 15, 2025 pm 12:21 PM

すべてのコンテナ(Docker PS)をリストする手順に従って、Dockerコンテナ名を照会できます。コンテナリストをフィルタリングします(GREPコマンドを使用)。コンテナ名(「名前」列にあります)を取得します。

Nginxバージョンを確認する方法 Nginxバージョンを確認する方法 Apr 14, 2025 am 11:57 AM

nginxバージョンを照会できるメソッドは次のとおりです。nginx-vコマンドを使用します。 nginx.confファイルでバージョンディレクティブを表示します。 nginxエラーページを開き、ページタイトルを表示します。

nginxでクラウドサーバードメイン名を構成する方法 nginxでクラウドサーバードメイン名を構成する方法 Apr 14, 2025 pm 12:18 PM

クラウドサーバーでnginxドメイン名を構成する方法:クラウドサーバーのパブリックIPアドレスを指すレコードを作成します。 NGINX構成ファイルに仮想ホストブロックを追加し、リスニングポート、ドメイン名、およびWebサイトルートディレクトリを指定します。 nginxを再起動して変更を適用します。ドメイン名のテスト構成にアクセスします。その他のメモ:SSL証明書をインストールしてHTTPSを有効にし、ファイアウォールがポート80トラフィックを許可し、DNS解像度が有効になることを確認します。

Windowsでnginxを構成する方法 Windowsでnginxを構成する方法 Apr 14, 2025 pm 12:57 PM

Windowsでnginxを構成する方法は? nginxをインストールし、仮想ホスト構成を作成します。メイン構成ファイルを変更し、仮想ホスト構成を含めます。 nginxを起動またはリロードします。構成をテストし、Webサイトを表示します。 SSLを選択的に有効にし、SSL証明書を構成します。ファイアウォールを選択的に設定して、ポート80および443のトラフィックを許可します。

Nginxが開始されるかどうかを確認する方法 Nginxが開始されるかどうかを確認する方法 Apr 14, 2025 pm 01:03 PM

nginxが開始されるかどうかを確認する方法:1。コマンドラインを使用します:SystemCTLステータスnginx(Linux/unix)、netstat -ano | FindStr 80(Windows); 2。ポート80が開いているかどうかを確認します。 3.システムログのnginx起動メッセージを確認します。 4. Nagios、Zabbix、Icingaなどのサードパーティツールを使用します。

Docker用のコンテナを作成する方法 Docker用のコンテナを作成する方法 Apr 15, 2025 pm 12:18 PM

Dockerでコンテナを作成します。1。画像を引く:Docker Pull [ミラー名]2。コンテナを作成:Docker Run [Options] [Mirror Name] [コマンド]3。コンテナを起動:Docker Start [Container Name]

nginxサーバーを開始する方法 nginxサーバーを開始する方法 Apr 14, 2025 pm 12:27 PM

NGINXサーバーを起動するには、異なるオペレーティングシステムに従って異なる手順が必要です。Linux/UNIXシステム:NGINXパッケージをインストールします(たとえば、APT-GetまたはYumを使用)。 SystemCtlを使用して、NGINXサービスを開始します(たとえば、Sudo SystemCtl Start NGinx)。 Windowsシステム:Windowsバイナリファイルをダウンロードしてインストールします。 nginx.exe実行可能ファイルを使用してnginxを開始します(たとえば、nginx.exe -c conf \ nginx.conf)。どのオペレーティングシステムを使用しても、サーバーIPにアクセスできます

Dockerによってコンテナを起動する方法 Dockerによってコンテナを起動する方法 Apr 15, 2025 pm 12:27 PM

Docker Containerの起動手順:コンテナ画像を引く:「Docker Pull [Mirror Name]」を実行します。コンテナの作成:「docker create [options] [mirror name] [コマンドとパラメーター]」を使用します。コンテナを起動します:「docker start [container name or id]」を実行します。コンテナのステータスを確認してください:コンテナが「Docker PS」で実行されていることを確認します。

See all articles