nginx での listen ディレクティブの例の分析

nginx での listen ディレクティブの例の分析

プロットレビュー

前回の記事では、ロケーション命令の解析プロセスを分析しました。この内容を簡単に確認してみましょう。各ロケーションは ngx_http_core_loc_conf_t 構造に対応しており、すべてのロケーションは双方向キューを介して接続されています。データ構造は比較的複雑です。

listen ディレクティブ

高性能 HTTP サーバーとして、ネットワーク処理は nginx の中核です。ネットワークの初期化を理解することで、nginx のネットワーク処理に対する理解が深まります。ネットワーク関連の主な構成コマンドは listen と server_name の 2 つです。 listen コマンドは、nginx のリスニング アドレスを設定します。IP プロトコルの場合、このアドレスはアドレスとポートです。UNIX ドメイン ソケット プロトコルの場合、このアドレスはパスです。listen 命令では、アドレスまたはポートを 1 つだけ指定できます。アドレスはホスト名にすることもできます。

この記事から、listen ディレクティブの解析プロセスを分析します。listen ディレクティブの構成は次のとおりです。nginx.org のマニュアルから、listen の使用方法を入手できます。

listenアドレス[:port] [default_server] [setfib=number] [backlog=number] [rcvbuf=size] [sndbuf=size] [accept_filter=filter] [deferred] [bind] [ipv6only=on|off] [ssl] [so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]];

listen 命令によって伝えられるパラメータは非常に複雑です。ただし、一般的にはあまり使用されないパラメータにはあまり注意が払われません。次に、一般的な設定方法をいくつか示します。

127.0.0.1:8000 をリッスンします。
ポートを追加せずに 127.0.0.1 を listen します。デフォルトのリスニング ポートは 80 です。
8000を聴く
聞く *:8000
ローカルホスト:8000を聴く

listenディレクティブのuriとポートを解析する

上記の内容から、listen には複数の用途があることがわかります。解析時には、listen ディレクティブのポート番号と uri 部分を取得する必要があります。Nginx は、uri とポートを解析するための ngx_parse_url() メソッドを提供します。この関数は、listen ディレクティブを解析するときに呼び出されます。

ngx_int_t
ngx_parse_url(ngx_pool_t *プール、ngx_url_t *u)
{
 u_char *p;
 size_t 長さ;

 p = u->url.data;
 len = u->url.len;
 // Unixドメインを解析するためのプロトコルは次のとおりです。if (len >= 5 && ngx_strncasecmp(p, (u_char *) "unix:", 5) == 0) {
 ngx_parse_unix_domain_url(pool, u) を返します。
 }
 // IPV6プロトコルを解析する if (len && p[0] == '[') {
 ngx_parse_inet6_url(pool, u) を返します。
 }
 // IPV4 プロトコルを解析します。 return ngx_parse_inet_url(pool, u);
}

IPV4プロトコルを使用し、ここではngx_parse_inet_url()関数を分析します。

// u.url = "80";
// u.listen = 1;
// u.default_port = 80;
静的 ngx_int_t
ngx_parse_inet_url(ngx_pool_t *pool、ngx_url_t *u) は、
{
 u_char *p、*ホスト、*ポート、*最終、*uri、*引数;
 size_t 長さ;
 ngx_int_t の引数は n です。
 構造体 sockaddr_in *sin;
#if (NGX_HAVE_INET6)
 構造体sockaddr_in6 *sin6;
#終了

 u->socklen = sizeof(struct sockaddr_in);
 sin = (struct sockaddr_in *) &u->sockaddr;
 sin->sin_family = AF_INET; //IPV4 タイプ u->family = AF_INET; 

 ホスト = u->url.data; // "80"

 last = host + u->url.len; // ホストの最後の文字の位置 port = ngx_strlchr(host, last, ':'); // ポートを検索します。ここでは NULL です

 uri = ngx_strlchr(host, last, '/'); // uri を検索します。ここは NULL です。

 args = ngx_strlchr(host, last, '?'); // ここでは NULL であるパラメータ args を検索します

 if (引数) {
 uri == NULL || args < uri の場合 {
 uri = 引数;
 }
 }

 if (uri) {
 if (u->listen || !u->uri_part) {
 u->err = "無効なホスト";
 NGX_ERROR を返します。
 }

 u->uri.len = 最後のuri;
 u->uri.data = uri;

 最後 = uri;

 URI < ポートの場合
 ポート = NULL;
 }
 }

 if (ポート) {
 ポート++;

 len = 最後のポート;

 n = ngx_atoi(ポート、長さ);

 (n < 1 || n > 65535)の場合{
 u->err = "無効なポート";
 NGX_ERROR を返します。
 }

 u->ポート = (in_port_t) n;
 sin->sin_port = htons((in_port_t) n);

 u->port_text.len = len;
 u->port_text.data = ポート;

 最後 = ポート - 1;

 } それ以外 {
 uri == NULLの場合{

 もし (u->listen) {

 /* ポートのみの値をテストします */

 n = ngx_atoi(ホスト、last - ホスト);

 (n != NGX_ERROR) の場合 {

 (n < 1 || n > 65535)の場合{
 u->err = "無効なポート";
 NGX_ERROR を返します。
 }

 u->ポート = (in_port_t) n;
 sin->sin_port = htons((in_port_t) n);

 u->port_text.len = 最終 - ホスト;
 u->port_text.data = ホスト;

 u->ワイルドカード = 1;

 NGX_OK を返します。
 }
 }
 }

 u->ポートなし = 1;
 u->ポート = u->default_port;
 sin->sin_port = htons(u->default_port);
 }

 len = 最後 - ホスト;

 長さが0の場合
 u->err = "ホストなし";
 NGX_ERROR を返します。
 }

 u->ホスト.len = len;
 u->host.data = ホスト;

 if (u->listen && len == 1 && *host == '*') {
 sin->sin_addr.s_addr = INADDR_ANY;
 u->ワイルドカード = 1;
 NGX_OK を返します。
 }

 sin->sin_addr.s_addr = ngx_inet_addr(ホスト、len);

 sin->sin_addr.s_addr != INADDR_NONE の場合 {

 sin->sin_addr.s_addr == INADDR_ANYの場合{
 u->ワイルドカード = 1;
 }

 u->naddrs = 1;

 u->addrs = ngx_pcalloc(プール、sizeof(ngx_addr_t));
 u->addrs == NULLの場合{
 NGX_ERROR を返します。
 }

 sin = ngx_pcalloc(プール、sizeof(struct sockaddr_in));
 (sin == NULL)の場合{
 NGX_ERROR を返します。
 }

 ngx_memcpy(sin, &u->sockaddr, sizeof(struct sockaddr_in));

 u->addrs[0].sockaddr = (struct sockaddr *) sin;
 u->addrs[0].socklen = sizeof(struct sockaddr_in);

 p = ngx_pnalloc(プール、u->host.len + sizeof(":65535") - 1);
 (p == NULL)の場合{
 NGX_ERROR を返します。
 }

 u->addrs[0].name.len = ngx_sprintf(p, "%V:%d",
  (u->ホスト、u->ポート) - p;
 u->addrs[0].name.data = p;

 NGX_OK を返します。
 }

 (u->解決しない場合){
 NGX_OK を返します。
 }

 ngx_inet_resolve_host(pool, u) != NGX_OK の場合 {
 NGX_ERROR を返します。
 }

 u->family = u->addrs[0].sockaddr->sa_family;
 u->socklen = u->addrs[0].socklen;
 ngx_memcpy(&u->sockaddr, u->addrs[0].sockaddr, u->addrs[0].socklen);

 スイッチ (u->family) {

#if (NGX_HAVE_INET6)
 AF_INET6の場合:
 sin6 = (struct sockaddr_in6 *) &u->sockaddr;

 (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) の場合 {
 u->ワイルドカード = 1;
 }

 壊す;
#終了

 デフォルト: /* AF_INET */
 sin = (struct sockaddr_in *) &u->sockaddr;

 sin->sin_addr.s_addr == INADDR_ANYの場合{
 u->ワイルドカード = 1;
 }

 壊す;
 }

 NGX_OK を返します。
}

この関数は、listen のアドレスとポート番号を解析します。構成ファイルでは、ポート番号は 80 で、listen アドレスは構成されていないため、u->wildcard = 1 となり、これはワイルドカードであり、サーバーのすべての IP アドレスのこのポート番号を listen することを示します。

listen ディレクティブの解析

ソースコードから listen 構成を見てみましょう。

{ 
 ngx_string("聞く"),
 NGX_HTTP_SRV_CONF|NGX_CONF_1MORE、
 ngx_http_core_listen、
 NGX_HTTP_SRV_CONF_OFFSET、
 0,
 NULL 
}

構成ファイルから、listen はサーバー モジュールにのみ出現し、複数のパラメーターを持つことができることがわかります。

対応する処理関数は ngx_http_core_listen です。この関数を解析してみましょう。エラー判定のため、一部コードを削除しています。

静的文字 *
ngx_http_core_listen(ngx_conf_t *cf、ngx_command_t *cmd、void *conf) は、
{
 ngx_http_core_srv_conf_t *cscf = conf;

 ngx_str_t *値、サイズ;
 ngx_url_t u;
 ngx_uint_t n;
 ngx_http_listen_opt_t lsopt;

 cscf->listen = 1;

 値 = cf->args->elts;

 ngx_memzero(&u, sizeof(ngx_url_t));

 u.url = 値[1];
 u.listen = 1;
 u.default_port = 80;

 ngx_parse_url(cf->pool, &u) != NGX_OK の場合 {
 NGX_CONF_ERROR を返します。
 }

 ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));

 ngx_memcpy(&lsopt.sockaddr.sockaddr, &u.sockaddr, u.socklen);

 lsopt.socklen = u.socklen;
 lsopt.backlog = NGX_LISTEN_BACKLOG;
 lsopt.rcvbuf = -1;
 lsopt.sndbuf = -1;
#if (NGX_HAVE_SETFIB)
 lsopt.setfib = -1;
#終了
#if (NGX_HAVE_TCP_FASTOPEN)
 lsopt.fastopen = -1;
#終了
 lsopt.ワイルドカード = u.ワイルドカード;
#if (NGX_HAVE_INET6)
 lsopt.ipv6only = 1;
#終了

 (void) ngx_sock_ntop(&lsopt.sockaddr.sockaddr, lsopt.socklen, lsopt.addr,
  NGX_SOCKADDR_STRLEN、1);

 (n = 2; n < cf->args->nelts; n++) の場合 {

 ngx_strcmp(値[n].data, "default_server") == 0の場合
 || ngx_strcmp(値[n].data, "デフォルト") == 0)
 {
 lsopt.default_server = 1;
 続く;
 }
 // ここでの他のコードはすべて listen のさまざまなパラメータを処理するためのもので、ここでの分析には役に立ちません}

 ngx_http_add_listen(cf, cscf, &lsopt) == NGX_OK の場合 {
 NGX_CONF_OK を返します。
 }

 NGX_CONF_ERROR を返します。
}

この関数の全体的なプロセスは、listen 命令のさまざまなパラメータを解析し、ngx_http_listen_opt_t を生成することです。名前が示すように、この構造体はいくつかのリスニング ポート オプション (リスニング ポート オプション) を保存するために使用されます。ここで関数 ngx_parse_url() が呼び出されます。これは上で分析しました。この関数の機能は、URL 内のアドレスとポートを解析することです。

次に最も重要な部分です。ngx_http_core_listen() 関数は最後に ngx_http_add_listen() 関数を呼び出し、これにより listen ポート情報が ngx_http_core_main_conf_t 構造体の ports 動的配列に保存されます。

ngx_http_add_listen() 関数

// cf: 設定構造 // cscf: listen ディレクティブが配置されているサーバーの設定構造 // lsopt: ngx_http_core_listen() によって生成された listen オプション
ngx_int_t
ngx_http_add_listen(ngx_conf_t *cf、ngx_http_core_srv_conf_t *cscf、
 ngx_http_listen_opt_t *lsopt)
{
 in_port_t p;
 ngx_uint_t i;
 構造体 sockaddr *sa;
 ngx_http_conf_port_t *ポート;
 ngx_http_core_main_conf_t *cmcf;
 // ngx_http_core_module モジュールの main_conf 構造を取得します ngx_http_core_main_conf_t
 cmcf = ngx_http_conf_get_module_main_conf(cf、ngx_http_core_module);
 // ポートフィールドは配列です if (cmcf->ports == NULL) {
  cmcf->ports = ngx_array_create(cf->temp_pool, 2,
          サイズ(ngx_http_conf_port_t));
  (cmcf->ports == NULL)の場合{
   NGX_ERROR を返します。
  }
 }

 sa = &lsopt->sockaddr.sockaddr;
 sa は ngx_inet_get_port というポート番号を持ちます。

 ポート = cmcf->ports->elts;
 (i = 0; i < cmcf->ports->nelts; i++) の場合 {

  p != port[i].port || sa->sa_family != port[i].family の場合 {
   続く;
  }

  /* ポートはすでにポートリストに存在します */

  ngx_http_add_addresses(cf, cscf, &port[i], lsopt) を返します。
 }

 /* ポートリストにポートを追加する */

 ポート = ngx_array_push(cmcf->ports);
 ポート == NULL の場合 {
  NGX_ERROR を返します。
 }

 ポートファミリ = sa->sa_family;
 ポート->ポート = p;
 port->addrs.elts = NULL;

 ngx_http_add_address(cf、cscf、port、lsopt) を返します。
}

この関数は、ポート番号情報を ngx_http_core_main_conf_t 構造体のポート フィールドに保存します。

要約する

上記はこの記事の全内容です。この記事の内容が皆さんの勉強や仕事に一定の参考学習価値を持つことを願っています。ご質問があれば、メッセージを残してコミュニケーションしてください。123WORDPRESS.COM を応援していただきありがとうございます。

以下もご興味があるかもしれません:
  • nginx ログ設定手順の詳細な説明
  • Nginx 構成ディレクティブの場所マッチャーの優先順位とセキュリティの問題
  • nginx proxy_pass ディレクティブ '/' の使用に関する注意事項
  • Nginx Gzipモジュールの有効化と設定手順の詳細な説明
  • nginx try_files ディレクティブはファイルが存在するかどうかを判定します
  • nginx add_headerディレクティブの使い方
  • nginx HTTPモジュール設定の一般的な手順
  • Nginxのmapコマンドを使用してページをリダイレクトする
  • Nginx 設定におけるルートとエイリアスのディレクティブの違いの簡単な分析
  • Nginx SSIディレクティブ設定の詳細な説明

<<:  Mysql のいくつかの複雑な SQL ステートメント (重複行のクエリと削除)

>>:  WeChatアプレットの世界的な状況の詳細な説明

推薦する

nginxリバースプロキシを介したデバッグコードの実装

背景現在、会社のプロジェクトは、フロントエンドとバックエンドが分離された方法で開発されています。新し...

テキストの展開と折りたたみの効果を実現するJavaScript

リスト形式のテキストの展開と折りたたみの実装は参考までに。具体的な内容は以下のとおりです。必要: 1...

JavaScript の手ぶれ補正とスロットリングの詳細な説明

目次デバウンススロットル要約するデバウンス定義: スクロール イベントなど、短時間に連続してトリガー...

CentOS7.6にMYSQL8.0をインストールする詳細な手順

1. 一般的に、CentOS では mariadb がデフォルトでインストールされているため、まず ...

Vue+WebSocket ページでの長時間接続のリアルタイム更新

最近、Vue プロジェクトではデータをリアルタイムで更新する必要があります。折れ線グラフは 1 秒ご...

MySQL における単一テーブルと複数テーブル、およびビューと一時テーブルに対する Update と Select の違い

1. テーブルAのデータを使用してMySQLのテーブルBの内容を更新するたとえば、データ テーブル内...

React を使って小さなプログラムを書くための Remax フレームワークのコンパイル プロセス分析 (推奨)

Remax は、実行時に構文制限のないソリューションを採用した React を使用して小規模なプロ...

Angularルーティングサブルートの詳細な説明

目次1. サブルート構文2. 例1. 2つの新しいコンポーネントを作成し、その内容を変更する2. ル...

Linux で指定されたフォルダの各サブフォルダ内のファイル数を表示する

カウントスクリプト #!/bin/sh 引数の数=$# [ $numOfArgs -ne 1 ]の場...

HTML5で見逃せないAPIやヒントのまとめ

これまでのブログ投稿では、HTML 5 ではあまり使われていないが注目すべき API やヒントに焦点...

MySQL 並列レプリケーションの簡単な分析

01 並列レプリケーションの概念MySQL のマスター スレーブ レプリケーション アーキテクチャで...

MySQL イベント スケジューラに関するよくある話 (必読)

概要MySQL には独自のイベント スケジューラもあり、これは Linux の crontab ジョ...

LAMP ソースコードを使用したエンタープライズレベルのインストールチュートリアル

目次LAMPアーキテクチャ1.ランプの紹介2. WebサービスワークフローWebサーバーのリソースは...

MySQL 5.7.19 インストールディレクトリに my.ini ファイルを作成する方法

前回の記事では、MySQL 5.7.19 無償インストール版 (64 ビット) の設定方法についての...

高性能なウェブサイトのための14のテクニック

オリジナル: http://developer.yahoo.com/performance/rule...