nginx のスムーズな再起動を実装する方法

nginx のスムーズな再起動を実装する方法

1. 背景

サーバーの開発プロセスでは、新しいコードや構成をロードするためにサービスを再起動することが避けられません。サーバーの再起動中にサービスが中断されないことが保証されれば、再起動によるビジネスへの影響をゼロにすることができます。最近、nginx のスムーズな再起動について調べて、とても興味深いと感じました。興味のある学生が読めるように記録しました。

2. プロセスを再起動する

  • 再起動とは、古いサーバーを新しいサーバーに置き換えることを意味します。タスクを引き継ぐプロセスでは、必然的に古いサーバーと新しいサーバーが共存することになります。したがって、再起動のプロセスはおおよそ次のようになります。
    • 新しいサーバーを起動する
    • 古いサーバーと新しいサーバーが共存し、両方がリクエストを処理してサービスを提供します。
    • 古いサーバーはすべてのリクエストを処理した後、正常に終了します。
  • ここでの主な問題は、古いサーバーと新しいサーバーが共存できるようにする方法です。再起動前と再起動後のサーバー ポートが同じである場合、両方が同じポートをリッスンできるようにするにはどうすればよいでしょうか。

3. nginxの実装

nginx のスムーズな再起動を検証するために、まず nginx の起動時に新しいサーバー インスタンスを再度起動してみました。結果は図のとおりです。

当然のことながら、古いサーバーと新しいサーバーは同じポート 80 を使用するため、サーバー インスタンスを再度開いても機能しません。ソケットの再利用ポート オプションが有効になっていない場合、ポートを再利用すると、bind システム コールは失敗します。デフォルトでは、nginx はバインドを 5 回再試行し、失敗するとすぐに終了します。 Nginx は IPV4 アドレス 0.0.0.0 と IPV6 アドレス [::] をリッスンする必要があるため、図には 10 個の emerg ログが出力されています。

次に、次の 2 つのコマンドで構成されるスムーズ再起動コマンドを試します。

kill -USR2 `cat /var/run/nginx.pid`
キル -QUIT `cat /var/run/nginx.pid.oldbin`

最初のコマンドは、古いマスター プロセスに USR2 シグナルを送信します。プロセスの pid は /var/run/nginx.pid ファイルに保存されます。nginx.pid ファイル パスは nginx.conf によって設定されます。

2 番目のコマンドは、古いマスター プロセスに QUIT シグナルを送信します。プロセスの pid は /var/run/nginx.pid.oldbin ファイルに保存され、古いマスター プロセスは終了します。

それで疑問なのは、なぜ古いマスター プロセスの pid が 2 つの pid ファイルに存在するのかということです。実際、古いマスター プロセスに USR2 シグナルを送信した後、古いマスター プロセスは pid の名前を変更し、元の nginx.pid ファイルの名前が nginx.pid.oldbin に変更されました。このようにして、新しいマスターはファイル名 nginx.pid を使用できます。

まず最初のコマンドを実行すると、結果は次のようになります。

はい、古いマスタープロセスと新しいマスタープロセスおよびワーカープロセスが共存します。 2 番目のコマンドを実行してみましょう。結果は次のようになります。

ご覧のとおり、古いマスター プロセス 8527 とそのワーカー プロセスはすべて終了し、新しいマスター プロセス 12740 だけが残っています。

新しいインスタンスを手動で起動しても機能しないのに、シグナルで再起動すると機能するのはなぜなのか、不思議に思わざるを得ません。まず、nginx ログ ファイルを確認します。

前のエラー ログに加えて、ソケットが継承され、fd 値が 6 と 7 であることを示す通知がもう 1 つあります。 ログに従って、nginx ソース コードを調べ、nginx.c/ngx_exec_new_binary 関数を見つけました。

ngx_pid_t
ngx_exec_new_binary(ngx_cycle_t *サイクル、char *const *argv)
{
  ...
  ctx.path = argv[0];
  ctx.name = "新しいバイナリプロセス";
  ctx.argv = argv;
  2;
  env = ngx_set_environment(サイクル、&n);
...
  var = ngx_alloc(sizeof(NGINX_VAR)
          + サイクル->listening.nelts * (NGX_INT32_LEN + 1) + 2、
          サイクル->ログ);
...
  p = ngx_cpymem(var, NGINX_VAR "=", sizeof(NGINX_VAR));
  ls = サイクル->listening.elts;
  (i = 0; i < cycle->listening.nelts; i++) {
    p = ngx_sprintf(p, "%ud;", ls[i].fd);
  }
  *p = '\0';
  env[n++] = var;
...
  env[n] = NULL;
...
  ctx.envp = (char *const *) env;
  ccf は ngx_core_conf_t に格納されます。
  ngx_rename_file(ccf->pid.data, ccf->oldpid.data) == NGX_FILE_ERROR の場合 {
    ...
    NGX_INVALID_PID を返します。
  }
  pid = ngx_execute(サイクル、&ctx);
  (pid == NGX_INVALID_PID)の場合{
    ngx_rename_file(ccf->oldpid.data, ccf->pid.data) の場合
      == NGX_FILE_ERROR)
    {
      ...
    }
  }
...
  pid を返します。
}

機能フローは

  1. 古いマスター プロセスによって監視されているすべての fd を、新しいマスター プロセスの env 環境変数 NGINX_VAR にコピーします。
  2. 名前を変更する pid ファイルの名前を変更する
  3. ngx_execute 関数は子プロセスをフォークし、execve はコマンド ラインを実行して新しいサーバーを起動します。
  4. サーバーの起動プロセスでは、環境変数 NGINX_VAR が解析されます。ngx_connection.c/ngx_add_inherited_sockets の具体的なコードは次のとおりです。
静的 ngx_int_t
ngx_add_inherited_sockets(ngx_cycle_t *cycle)
{
...
  継承 = (u_char *) getenv(NGINX_VAR);
  if (継承 == NULL) {
    NGX_OK を返します。
  }
  if (ngx_array_init(&cycle->listening, cycle->pool, 10,
            サイズ(ngx_listening_t)
    != NGX_OK)
  {
    NGX_ERROR を返します。
  }
  (p = 継承、v = p; *p; p++) {
    (*p == ':' || *p == ';') の場合 {
      s = ngx_atoi(v, p - v);
      ...
      v = p + 1;
      ls = ngx_array_push(&cycle->listening);
      ls == NULLの場合{
        NGX_ERROR を返します。
      }
      ngx_memzero(ls、sizeof(ngx_listening_t));
      ls->fd = (ngx_socket_t) s;
    }
  }
  ...
  ngx_継承 = 1;
  ngx_set_inherited_sockets(cycle) を返します。
}

機能フローは次のとおりです。

環境変数NGINX_VARの値を解析し、配列に格納するfdを取得します。

これらのソケットの情報を保存するために、fd に対応するソケットが ngx_inherited に設定されます。

つまり、新しいサーバーは listen ポートをまったく再バインドしません。これらの fd の状態と値は、新しいマスター プロセスがフォークしたときに引き継がれます。新しいマスター プロセスは、継承されたファイル記述子を listen して処理できます。ここで重要な点は、listen ソケットのファイル記述子が ENV を介して渡されることです。

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

以下もご興味があるかもしれません:
  • nginx のスムーズな再起動とアップグレードを実装する方法
  • Nginxスムーズアップグレードの詳しい操作方法
  • Nginx 1.8.0 バージョンが新しいバージョン 1.9.7 にスムーズにアップグレードされました
  • 1 分で Nginx のバージョンをスムーズにアップグレードおよびロールバックする方法
  • nginxのスムーズなアップグレードのプロセスを詳しく説明
  • nginx のスムーズな再起動とスムーズなアップグレードのグラフィックチュートリアル

<<:  JSはGMTとUTCのタイムゾーンを完全に理解しています

>>:  MySQL クイックデータ比較テクニック

推薦する

MySQLdump コマンドを使用した MySQL データの移行

このソリューションの利点はシンプルさと使いやすさですが、欠点はダウンタイムが長くなることです。 した...

HTML 特殊文字エンコーディング CSS3 コンテンツに関する簡単な説明:「私は特別なシンボルです」

プロジェクトで使用されている特殊文字とアイコンHTMLコードXML/HTML コードコンテンツをクリ...

HTML と CSS の基礎 (必読)

(1) HTML: ハイパーテキストマークアップ言語。主に「ヘッダー」と「ボディ」の2つの部分で構...

Linux の一般的なハードディスク管理コマンドの紹介

目次1. dfコマンド2. duコマンド3. fsckファイルシステム修復コマンド4. ディスクステ...

JSはクリックドロップ効果を実装します

jsはクリックとドロップの特殊効果を実現します。まずは効果画像を見てみましょうさっそく始めましょう。...

実践的な経験を共有するためのコードチェックツールstylelintの紹介

目次序文文章1. stylelintをインストールする2. 設定ファイル3. stylelintを使...

画像マーキー効果を実現するネイティブJS

今日は、ネイティブ JS で実装された画像マーキー効果を紹介します。効果は次のとおりです。 実装され...

この記事は、JQueryの基本的な操作を理解し、始めるのに役立ちます。

目次1. Jquery を使用する手順: (1)jsライブラリをインポートする(2)ページ読み込みイ...

vue-element-admin グローバル読み込み待機中

最近の要件:グローバルロード、すべてのインターフェースはロード待機機能を表示するかどうかを手動で制御...

Ubuntu 16.04 にソースコードから Mininet をインストールする

ミニネットMininet は軽量のソフトウェア定義ネットワークおよびテスト プラットフォームです。軽...

HTML ページに SVG を挿入する複数の方法

SVG (Scalable Vector Graphics)は、XML 構文に基づいた画像形式です。...

JavaScript でローカル変数をグローバル変数に変換する方法

まず関数の自己呼び出しを知る必要がある関数の自己呼び出し - 自己呼び出し関数1 回限りの関数 - ...

ウェブページをデザインする際に注意すべきいくつかの問題

Web デザインは、個人の好みや Web ページの内容に応じて、デザインのレイアウトが常に変化します...

MySQL オンラインリカバリ UNDO テーブルスペース 実戦記録

1 MySQL5.6 1.1 関連パラメータMySQL 5.6 では、innodb_undo_dir...

クールなネオンライト効果を実現する純粋な CSS (デモ付き)

私は最近、YouTube の CSS アニメーション効果チュートリアル シリーズをフォローしています...