nginx HTTP 処理フローの簡単な分析
この記事は、nginx の HTTP 処理プロセスの簡単な分析を提供します。これには一定の参考値があります。必要な友人は参照できます。お役に立てれば幸いです。
1. サーバーの初期化
server コマンドは、仮想サーバーを構成するために使用されます。通常、異なるサーバーをリッスンするために 1 台のマシン上に複数の仮想サーバーを構成します。ポート番号。異なるファイル ディレクトリにマッピングされます。nginx はユーザー設定を解析し、すべてのポートにソケットを作成し、リッスンを開始します。
nginx 解析設定ファイルは、各モジュールによって共有され、処理されます。各モジュールは、フィールド ngx_command_t *モジュール構造体 ngx_module_t のコマンドを通じて実装される、対象となる設定を登録して処理します。たとえば、ngx_http_module はコア モジュールであり、そのコマンド フィールドは次のように定義されます:
struct ngx_command_s { ngx_str_t name; ngx_uint_t type; char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); }; static ngx_command_t ngx_http_commands[] = { { ngx_string("http"), NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, ngx_http_block, }, };
- name コマンド名。これは、構成ファイルを解析するときに名前に従って検索できます。
- #type 命令タイプ、NGX_CONF_NOARGS、この構成にはパラメータがありません、NGX_CONF_BLOCK、この構成は構成ブロックです、NGX_MAIN_CONF は、構成がどのビットに表示されるかを示します (NGX_MAIN_CONF、NGX_HTTP_SRV_CONF、NGX_HTTP_LOC_CONF);
- set コマンド処理関数ポインタ ;
- http 命令を解析するための処理関数が ngx_http_block であることがわかります。これは次のように実装されています。
static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { //解析main配置 //解析server配置 //解析location配置 //初始化HTTP处理流程所需的handler //初始化listening if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) { return NGX_CONF_ERROR; } }
ngx_listening_t のハンドラーが ngx_http_init_connection に設定されていることに注意してください。ソケット接続リクエストが受信されると、このハンドラーが呼び出されます。
static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle) { ls = cycle->listening.elts; for (i = 0; i listening.nelts; i++) { //设置读事件处理handler rev->handler = ngx_event_accept; ngx_add_event(rev, NGX_READ_EVENT, 0); } }
2.HTTP リクエスト分析
2.1 基本構造
構造 ngx_connection_t はソケット接続関連の情報を保存します; nginx は事前にいくつかの ngx_connection_t オブジェクトを作成します。グローバル変数 ngx_cycle->free_connections では、接続プールと呼ばれます。新しいソケットが生成されると、接続プールからアイドル状態の接続を取得しようとします。取得に失敗した場合、ソケットは直接閉じられます。 命令 worker_connections は、接続プール内の接続の最大数を構成するために使用されます。これは、イベント命令ブロックで構成され、ngx_event_core_module によって解析されます。vents { use epoll; worker_connections 60000; }
worker_connections/2; nginx がリバース プロキシ サーバーとして機能する場合、最大クライアント数 maxClient=worker_processes
ワーカー接続/4 。そのworker_processesは、ユーザーによって構成されたワーカープロセスの数です。構造体 ngx_connection_t は次のように定義されています:
struct ngx_connection_s { //空闲连接池中,data指向下一个连接,形成链表;取出来使用时,data指向请求结构体ngx_http_request_s void *data; //读写事件结构体,两个关键字段:handler处理函数、timer定时器 ngx_event_t *read; ngx_event_t *write; ngx_socket_t fd; //socket fd ngx_recv_pt recv; //socket接收数据函数指针 ngx_send_pt send; //socket发送数据函数指针 ngx_buf_t *buffer; //输入缓冲区 struct sockaddr *sockaddr; //客户端地址 socklen_t socklen; ngx_listening_t *listening; //监听的ngx_listening_t对象 struct sockaddr *local_sockaddr; //本地地址 socklen_t local_socklen; ………… }
struct ngx_http_request_s { ngx_connection_t *connection; //读写事件处理handler ngx_http_event_handler_pt read_event_handler; ngx_http_event_handler_pt write_event_handler; //请求头缓冲区 ngx_buf_t *header_in; //解析后的请求头 ngx_http_headers_in_t headers_in; //请求体结构体 ngx_http_request_body_t *request_body; //请求行 ngx_str_t request_line; //解析后请求行若干字段 ngx_uint_t method; ngx_uint_t http_version; ngx_str_t uri; ngx_str_t args; ………… }
ngx_http_header_t ngx_http_headers_in[] = { { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host), ngx_http_process_host }, { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection), ngx_http_process_connection }, ………… } typedef struct { ngx_str_t name; ngx_uint_t offset; ngx_http_header_handler_pt handler; } ngx_http_header_t;
static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h, ngx_uint_t offset) { if (ngx_strcasestrn(h->value.data, "close", 5 - 1)) { r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; } else if (ngx_strcasestrn(h->value.data, "keep-alive", 10 - 1)) { r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE; } return NGX_OK; }
typedef struct { ngx_uint_t hash; //请求头key的hash值 ngx_str_t key; ngx_str_t value; u_char *lowcase_key; //请求头key转为小写字符串(可以看到HTTP请求头解析时key不区分大小写) } ngx_table_elt_t;
2.2 解析HTTP请求
第1节提到,在创建socket启动监听时,会添加可读事件到epoll,事件处理函数为ngx_event_accept,用于接收socket连接,分配connection连接,并调用ngx_listening_t对象的处理函数(ngx_http_init_connection)。
void ngx_event_accept(ngx_event_t *ev) { s = accept4(lc->fd, (struct sockaddr *) sa, &socklen, SOCK_NONBLOCK); //客户端socket连接成功时,都需要分配connection连接,如果分配失败则会直接关闭此socket。 //而每个worker进程连接池的最大连接数目是固定的,当不存在空闲连接时,此worker进程accept的所有socket都会被拒绝; //多个worker进程通过竞争执行epoll_wait;而当ngx_accept_disabled大于0时,会直接放弃此次竞争,同时ngx_accept_disabled减1。 //以此实现,当worker进程的空闲连接过少时,减少其竞争epoll_wait次数 ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n; c = ngx_get_connection(s, ev->log); ls->handler(c); }
socket连接成功后,nginx会等待客户端发送HTTP请求,默认会有60秒的超时时间,即60秒内没有接收到客户端请求时,断开此连接,打印错误日志。函数ngx_http_init_connection用于设置读事件处理函数,以及超时定时器。
void ngx_http_init_connection(ngx_connection_t *c) { c->read = ngx_http_wait_request_handler; c->write->handler = ngx_http_empty_handler; ngx_add_timer(rev, c->listening->post_accept_timeout); }
全局搜索post_accept_timeout字段,可以查找到设置此超时时间的配置指令,client_header_timeout,其可以在http、server指令块中配置。
函数ngx_http_wait_request_handler为解析HTTP请求的入口函数,实现如下:
static void ngx_http_wait_request_handler(ngx_event_t *rev) { //读事件已经超时 if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); ngx_http_close_connection(c); return; } size = cscf->client_header_buffer_size; //client_header_buffer_size指令用于配置接收请求头缓冲区大小 b = c->buffer; n = c->recv(c, b->last, size); //创建请求对象ngx_http_request_t,HTTP请求整个处理过程都有用; c->data = ngx_http_create_request(c); rev->handler = ngx_http_process_request_line; //设置读事件处理函数(此次请求行可能没有读取完) ngx_http_process_request_line(rev); }
函数ngx_http_create_request创建并初始化ngx_http_request_t对象,注意这赋值语句r->header_in =c->buffer。
解析请求行与请求头的代码较为繁琐,终点在于读取socket数据,解析字符串,这里不做详述。HTTP请求解析过程主要函数调用如下图所示:
注意,解析完成请求行与请求头,nginx就开始处理HTTP请求,并没有等到解析完请求体再处理。处理请求入口为ngx_http_process_request。
3.处理HTTP请求
3.1 HTTP请求处理的11个阶段
nginx将HTTP请求处理流程分为11个阶段,绝大多数HTTP模块都会将自己的handler添加到某个阶段(将handler添加到全局唯一的数组phases中),注意其中有4个阶段不能添加自定义handler,nginx处理HTTP请求时会挨个调用每个阶段的handler;
typedef enum { NGX_HTTP_POST_READ_PHASE = 0, //第一个阶段,目前只有realip模块会注册handler,但是该模块默认不会运行(nginx作为代理服务器时有用,后端以此获取客户端原始ip) NGX_HTTP_SERVER_REWRITE_PHASE, //server块中配置了rewrite指令,重写url NGX_HTTP_FIND_CONFIG_PHASE, //查找匹配的location配置;不能自定义handler; NGX_HTTP_REWRITE_PHASE, //location块中配置了rewrite指令,重写url NGX_HTTP_POST_REWRITE_PHASE, //检查是否发生了url重写,如果有,重新回到FIND_CONFIG阶段;不能自定义handler; NGX_HTTP_PREACCESS_PHASE, //访问控制,比如限流模块会注册handler到此阶段 NGX_HTTP_ACCESS_PHASE, //访问权限控制,比如基于ip黑白名单的权限控制,基于用户名密码的权限控制等 NGX_HTTP_POST_ACCESS_PHASE, //根据访问权限控制阶段做相应处理;不能自定义handler; NGX_HTTP_TRY_FILES_PHASE, //只有配置了try_files指令,才会有此阶段;不能自定义handler; NGX_HTTP_CONTENT_PHASE, //内容产生阶段,返回响应给客户端 NGX_HTTP_LOG_PHASE //日志记录 } ngx_http_phases;
nginx使用结构体ngx_module_s表示一个模块,其中字段ctx,是一个指向模块上下文结构体的指针(上下文结构体的字段都是一些函数指针);nginx的HTTP模块上下文结构体大多都有字段postconfiguration,负责注册本模块的handler到某个处理阶段。11个阶段在解析完成http配置块指令后初始化。
static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { //解析http配置块 //初始化11个阶段的phases数组,注意多个模块可能注册到同一个阶段,因此phases是一个二维数组 if (ngx_http_init_phases(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } //遍历索引HTTP模块,注册handler for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_HTTP_MODULE) { continue; } module = ngx_modules[m]->ctx; if (module->postconfiguration) { if (module->postconfiguration(cf) != NGX_OK) { return NGX_CONF_ERROR; } } } //将二维数组转换为一维数组,从而遍历执行数组所有handler if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } }
以限流模块ngx_http_limit_req_module模块为例,postconfiguration方法简单实现如下:
static ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf) { h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers); *h = ngx_http_limit_req_handler; //ngx_http_limit_req_module模块的限流方法;nginx处理HTTP请求时,都会调用此方法判断应该继续执行还是拒绝请求 return NGX_OK; }
GDB调试,断点到ngx_http_block方法执行所有HTTP模块注册handler之后,打印phases数组
p cmcf->phases[*].handlers p *(ngx_http_handler_pt*)cmcf->phases[*].handlers.elts
11个阶段注册的handler如下图所示:
3.2 11个阶段初始化
上面提到HTTP的11个处理阶段handler存储在phases数组,但由于多个模块可能注册handler到同一个阶段,使得phases是一个二维数组,因此需要转换为一维数组,转换后存储在cmcf->phase_engine字段,phase_engine的类型为ngx_http_phase_engine_t,定义如下:
typedef struct { ngx_http_phase_handler_t *handlers; //一维数组,存储所有handler ngx_uint_t server_rewrite_index; //记录NGX_HTTP_SERVER_REWRITE_PHASE阶段handler的索引值 ngx_uint_t location_rewrite_index; //记录NGX_HTTP_REWRITE_PHASE阶段handler的索引值 } ngx_http_phase_engine_t; struct ngx_http_phase_handler_t { ngx_http_phase_handler_pt checker; //执行handler之前的校验函数 ngx_http_handler_pt handler; ngx_uint_t next; //下一个待执行handler的索引(通过next实现handler跳转执行) }; //cheker函数指针类型定义 typedef ngx_int_t (*ngx_http_phase_handler_pt)(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); //handler函数指针类型定义 typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);
数组转换函数ngx_http_init_phase_handlers实现如下:
static ngx_int_t ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf) { use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0; use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0; n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */; //至少有4个阶段,这4个阶段是上面说的不能注册handler的4个阶段 //计算handler数目,分配空间 for (i = 0; i phases[i].handlers.nelts; } ph = ngx_pcalloc(cf->pool, n * sizeof(ngx_http_phase_handler_t) + sizeof(void *)); //遍历二维数组 for (i = 0; i phases[i].handlers.elts; switch (i) { case NGX_HTTP_SERVER_REWRITE_PHASE: if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) { cmcf->phase_engine.server_rewrite_index = n; //记录NGX_HTTP_SERVER_REWRITE_PHASE阶段handler的索引值 } checker = ngx_http_core_rewrite_phase; break; case NGX_HTTP_FIND_CONFIG_PHASE: find_config_index = n; //记录NGX_HTTP_FIND_CONFIG_PHASE阶段的索引,NGX_HTTP_POST_REWRITE_PHASE阶段可能会跳转回此阶段 ph->checker = ngx_http_core_find_config_phase; n++; ph++; continue; //进入下一个阶段NGX_HTTP_REWRITE_PHASE case NGX_HTTP_REWRITE_PHASE: if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) { cmcf->phase_engine.location_rewrite_index = n; //记录NGX_HTTP_REWRITE_PHASE阶段handler的索引值 } checker = ngx_http_core_rewrite_phase; break; case NGX_HTTP_POST_REWRITE_PHASE: if (use_rewrite) { ph->checker = ngx_http_core_post_rewrite_phase; ph->next = find_config_index; n++; ph++; } continue; //进入下一个阶段NGX_HTTP_ACCESS_PHASE case NGX_HTTP_ACCESS_PHASE: checker = ngx_http_core_access_phase; n++; break; case NGX_HTTP_POST_ACCESS_PHASE: if (use_access) { ph->checker = ngx_http_core_post_access_phase; ph->next = n; ph++; } continue; //进入下一个阶段 case NGX_HTTP_TRY_FILES_PHASE: if (cmcf->try_files) { ph->checker = ngx_http_core_try_files_phase; n++; ph++; } continue; case NGX_HTTP_CONTENT_PHASE: checker = ngx_http_core_content_phase; break; default: checker = ngx_http_core_generic_phase; } //n为下一个阶段第一个handler的索引 n += cmcf->phases[i].handlers.nelts; //遍历当前阶段的所有handler for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--) { ph->checker = checker; ph->handler = h[j]; ph->next = n; ph++; } } }
GDB打印出转换后的数组如下图所示,第一列是cheker字段,第二列是handler字段,箭头表示next跳转;图中有个返回的箭头,即NGX_HTTP_POST_REWRITE_PHASE阶段可能返回到NGX_HTTP_FIND_CONFIG_PHASE;原因在于只要NGX_HTTP_REWRITE_PHASE阶段产生了url重写,就需要重新查找匹配location。
3.3 处理HTTP请求
2.2节提到HTTP请求的处理入口函数是ngx_http_process_request,其主要调用ngx_http_core_run_phases实现11个阶段的执行流程;
ngx_http_core_run_phases遍历预先设置好的cmcf->phase_engine.handlers数组,调用其checker函数,逻辑如下:
void ngx_http_core_run_phases(ngx_http_request_t *r) { ph = cmcf->phase_engine.handlers; //phase_handler初始为0,表示待处理handler的索引;cheker内部会根据ph->next字段修改phase_handler while (ph[r->phase_handler].checker) { rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); if (rc == NGX_OK) { return; } } }
checker内部就是调用handler,并设置下一步要执行handler的索引;比如说ngx_http_core_generic_phase实现如下:
ngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "rewrite phase: %ui", r->phase_handler); rc = ph->handler(r); if (rc == NGX_OK) { r->phase_handler = ph->next; return NGX_AGAIN; } }
3.4 内容产生阶段
内容产生阶段NGX_HTTP_CONTENT_PHASE是HTTP请求处理的第10个阶段,一般情况有3个模块注册handler到此阶段:ngx_http_static_module、ngx_http_autoindex_module和ngx_http_index_module。
但是当我们配置了proxy_pass和fastcgi_pass时,情况会有所不同;
使用proxy_pass配置上游时,ngx_http_proxy_module模块会设置其处理函数到配置类conf;使用fastcgi_pass配置时,ngx_http_fastcgi_module会设置其处理函数到配置类conf。例如:
static char * ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *clcf; clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_fastcgi_handler; }
阶段NGX_HTTP_FIND_CONFIG_PHASE查找匹配的location,并获取此ngx_http_core_loc_conf_t对象,将其handler赋值给ngx_http_request_t对象的content_handler字段(内容产生处理函数)。
而在执行内容产生阶段的checker函数时,会执行content_handler指向的函数;查看ngx_http_core_content_phase函数实现(内容产生阶段的checker函数):
ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { if (r->content_handler) { //如果请求对象的content_handler字段不为空,则调用 r->write_event_handler = ngx_http_request_empty_handler; ngx_http_finalize_request(r, r->content_handler(r)); return NGX_OK; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "content phase: %ui", r->phase_handler); rc = ph->handler(r); //否则执行内容产生阶段handler }
总结
nginx处理HTTP请求的流程较为复杂,因此本文只是简单提供了一条线索:分析了nginx服务器启动监听的过程,HTTP请求的解析过程,11个阶段的初始化与调用过程。至于HTTP解析处理的详细流程,还需要读者去探索。
以上がnginx HTTP 処理フローの簡単な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

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

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

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

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

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

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

ホットトピック









Tomcat サーバーが外部ネットワークにアクセスできるようにするには、以下を行う必要があります。 外部接続を許可するように Tomcat 構成ファイルを変更します。 Tomcat サーバー ポートへのアクセスを許可するファイアウォール ルールを追加します。 Tomcat サーバーのパブリック IP を指すドメイン名を指す DNS レコードを作成します。オプション: リバース プロキシを使用して、セキュリティとパフォーマンスを向上させます。オプション: セキュリティを強化するために HTTPS を設定します。

HTML ファイルを URL に変換するには Web サーバーが必要です。これには次の手順が含まれます。 Web サーバーを取得します。 Webサーバーをセットアップします。 HTMLファイルをアップロードします。ドメイン名を作成します。リクエストをルーティングします。

Node.js プロジェクトのサーバー デプロイメント手順: デプロイメント環境を準備します。サーバー アクセスの取得、Node.js のインストール、Git リポジトリのセットアップ。アプリケーションをビルドする: npm run build を使用して、デプロイ可能なコードと依存関係を生成します。コードをサーバーにアップロードします: Git またはファイル転送プロトコル経由。依存関係をインストールする: サーバーに SSH で接続し、npm install を使用してアプリケーションの依存関係をインストールします。アプリケーションを開始します。node Index.js などのコマンドを使用してアプリケーションを開始するか、pm2 などのプロセス マネージャーを使用します。リバース プロキシの構成 (オプション): Nginx や Apache などのリバース プロキシを使用して、トラフィックをアプリケーションにルーティングします。

はい、Node.js には外部からアクセスできます。次の方法を使用できます。 Cloud Functions を使用して関数をデプロイし、一般にアクセスできるようにします。 Express フレームワークを使用してルートを作成し、エンドポイントを定義します。 Nginx を使用して、Node.js アプリケーションへのリバース プロキシ リクエストを実行します。 Docker コンテナを使用して Node.js アプリケーションを実行し、ポート マッピングを通じて公開します。

PHP Web サイトを正常に展開して維持するには、次の手順を実行する必要があります。 Web サーバー (Apache や Nginx など) を選択する PHP をインストールする データベースを作成して PHP に接続する コードをサーバーにアップロードする ドメイン名と DNS を設定する Web サイトのメンテナンスを監視する手順には、PHP および Web サーバーの更新、Web サイトのバックアップ、エラー ログの監視、コンテンツの更新が含まれます。

Linux 管理者にとっての重要なタスクは、サーバーを違法な攻撃やアクセスから保護することです。デフォルトでは、Linux システムには、iptables、Uncomplicated Firewall (UFW)、ConfigServerSecurityFirewall (CSF) などの適切に構成されたファイアウォールが付属しており、さまざまな攻撃を防ぐことができます。インターネットに接続されているマシンはすべて、悪意のある攻撃のターゲットになる可能性があります。サーバーへの不正アクセスを軽減するために使用できる Fail2Ban と呼ばれるツールがあります。 Fail2Ban とは何ですか? Fail2Ban[1] は、ブルート フォース攻撃からサーバーを保護する侵入防止ソフトウェアです。 Python プログラミング言語で書かれています

今回は、Linux 環境に Nginx をインストールする方法を説明します。 ここで使用する Linux システムは CentOS7.2 です。 インストール ツールを準備します。 1. Nginx 公式 Web サイトから Nginx をダウンロードします。ここで使用するバージョンは 1.13.6.2. ダウンロードした Nginx を Linux にアップロードする ここでは例として /opt/nginx ディレクトリを使用します。 「tar-zxvfnginx-1.13.6.tar.gz」を実行して解凍します。 3. /opt/nginx/nginx-1.13.6 ディレクトリに切り替え、./configure を実行して初期設定を行います。次のプロンプトが表示された場合は、マシンに PCRE がインストールされていないため、Nginx がインストールする必要があることを意味します。

yum が keepalived をインストールした後、keepalived 設定ファイルを設定します。マスターおよびバックアップの keepalived 設定ファイルでは、通常、高可用性では VIP のネットワーク カード名が選択されています。 LAN 環境 他にもあるため、この VIP は 2 台のマシンと同じネットワーク セグメント内のイントラネット IP です。外部ネットワーク環境で使用する場合、クライアントがアクセスできれば同一ネットワークセグメント上にあるかは関係ありません。 nginx サービスを停止し、keepalived サービスを開始します。 keepalived が nginx サービスを開始できないことがわかりますが、それは基本的に設定ファイルとスクリプトに問題があるか、防止の問題です。
