<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ### 概述 ? ? ? 在上一篇文章《 [Nginx 啟動初始化過程](http://blog.csdn.net/chenhanzhun/article/details/42611315)》簡單介紹了 Nginx 啟動的過程,并分析了其啟動過程的源碼。在啟動過程中有一個步驟非常重要,就是調用函數?ngx_init_cycle(),該函數的調用為配置解析提供了接口。配置解析接口大概可分為兩個階段:**準備數據階段**和**配置解析階段**; ? ? ? 準備數據階段包括: - 準備內存; - 準備錯誤日志; - 準備所需數據結構; ? ? ? 配置解析階段是調用函數: ~~~ /* 配置文件解析 */ if (ngx_conf_param(&conf) != NGX_CONF_OK) {/* 帶有命令行參數'-g' 加入的配置 */ environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; } if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {/* 解析配置文件*/ environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; } ~~~ ### 配置解析 ### ngx_conf_t 結構體 ? ? ? 該結構體用于 Nginx 在解析配置文件時描述每個指令的屬性,也是Nginx 程序中非常重要的一個數據結構,其定義于文件:[src/core/ngx_conf_file.h](http://lxr.nginx.org/source/src/core/ngx_conf_file.h) ~~~ /* 解析配置時所使用的結構體 */ struct ngx_conf_s { char *name; /* 當前解析到的指令 */ ngx_array_t *args; /* 當前指令所包含的所有參數 */ ngx_cycle_t *cycle; /* 待解析的全局變量ngx_cycle_t */ ngx_pool_t *pool; /* 內存池 */ ngx_pool_t *temp_pool;/* 臨時內存池,分配一些臨時數組或變量 */ ngx_conf_file_t *conf_file;/* 待解析的配置文件 */ ngx_log_t *log; /* 日志信息 */ void *ctx; /* 描述指令的上下文 */ ngx_uint_t module_type;/* 當前解析的指令的模塊類型 */ ngx_uint_t cmd_type; /* 當前解析的指令的指令類型 */ ngx_conf_handler_pt handler; /* 模塊自定義的handler,即指令自定義的處理函數 */ char *handler_conf;/* 自定義處理函數需要的相關配置 */ }; ~~~ #### 配置文件信息 conf_file ? ? ? conf_file?是存放 Nginx 配置文件的相關信息。ngx_conf_file_t 結構體的定義如下: ~~~ typedef struct { ngx_file_t file; /* 文件的屬性 */ ngx_buf_t *buffer; /* 文件的內容 */ ngx_uint_t line; /* 文件的行數 */ } ngx_conf_file_t; ~~~ #### 配置上下文 ctx ? ? ? Nginx 的配置文件是分塊配置的,常見的有http 塊、server 塊、location 塊以及upsteam 塊和 mail 塊等。每一個這樣的配置塊代表一個作用域。高一級配置塊的作用域包含了多個低一級配置塊的作用域,也就是有作用域嵌套的現象。這樣,配置文件中的許多指令都會同時包含在多個作用域內。比如,http 塊中的指令都可能同時處于http 塊、server 塊和location 塊等三層作用域內。 ? ? ? 在 Nginx 程序解析配置文件時,每一條指令都應該記錄自己所屬的作用域范圍,而配置文件上下文ctx 變量就是用來存放當前指令所屬的作用域的。在Nginx 配置文件的各種配置塊中,http 塊可以包含子配置塊,這在存儲結構上比較復雜。 #### 指令類型 type ? ? ? Nginx 程序中的不同的指令類型以宏的形式定義在不同的源碼頭文件中,指令類型是core 模塊類型的定義在文件:[src/core/ngx_conf_file.h](http://lxr.nginx.org/source/src/core/ngx_conf_file.h) ~~~ #define NGX_DIRECT_CONF 0x00010000 #define NGX_MAIN_CONF 0x01000000 #define NGX_ANY_CONF 0x0F000000 ~~~ ? ? ? 這些是?core?類型模塊支持的指令類型。其中的?NGX_DIRECT_CONF類指令在?Nginx?程序進入配置解析函數之前已經初始化完成,所以在進入配置解析函數之后可以將它們直接解析并存儲到實際的數據結構中,從配置文件的結構上來看,它們一般指的就是那些游離于配置塊之外、處于配置文件全局塊部分的指令。NGX_MAIN_CONF 類指令包括event、http、mail、upstream?等可以形成配置塊的指令,它們沒有自己的初始化函數。Nginx 程序在解析配置文件時如果遇到 NGX_MAIN_CONF 類指令,將轉入對下一級指令的解析。 ? ? ? 以下是?event?類型模塊支持的指令類型。 ~~~ #define NGX_EVENT_CONF 0x02000000 ~~~ ? ? ? 以下是 http 類型模塊支持的指令類型,其定義在文件:[src/http/ngx_http_config.h](http://lxr.nginx.org/source/src/http/ngx_http_config.h) ~~~ #define NGX_HTTP_MAIN_CONF 0x02000000 #define NGX_HTTP_SRV_CONF 0x04000000 #define NGX_HTTP_LOC_CONF 0x08000000 #define NGX_HTTP_UPS_CONF 0x10000000 #define NGX_HTTP_SIF_CONF 0x20000000 #define NGX_HTTP_LIF_CONF 0x40000000 #define NGX_HTTP_LMT_CONF 0x80000000 ~~~ ### 通用模塊配置解析 ? ? ? 配置解析模塊在 [src/core/ngx_conf_file.c](http://lxr.nginx.org/source/src/core/ngx_conf_file.c) 中實現。模塊提供的接口函數主要是ngx_conf_parse。另外,模塊提供另一個單獨的接口ngx_conf_param,用來解析命令行傳遞的配置,這個接口也是對ngx_conf_parse 的包裝。首先看下配置解析函數?ngx_conf_parse,其定義如下: ~~~ /* * 函數功能:配置文件解析; * 支持三種不同的解析類型: * 1、解析配置文件; * 2、解析block塊設置; * 3、解析命令行配置; */ char * ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename) { char *rv; ngx_fd_t fd; ngx_int_t rc; ngx_buf_t buf; ngx_conf_file_t *prev, conf_file; enum { parse_file = 0, parse_block, parse_param } type; #if (NGX_SUPPRESS_WARN) fd = NGX_INVALID_FILE; prev = NULL; #endif if (filename) {/* 若解析的是配置文件 */ /* open configuration file */ /* 打開配置文件 */ fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); if (fd == NGX_INVALID_FILE) { ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, ngx_open_file_n " \"%s\" failed", filename->data); return NGX_CONF_ERROR; } prev = cf->conf_file; cf->conf_file = &conf_file; if (ngx_fd_info(fd, &cf->conf_file->file.info) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, ngx_fd_info_n " \"%s\" failed", filename->data); } cf->conf_file->buffer = &buf; buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log); if (buf.start == NULL) { goto failed; } buf.pos = buf.start; buf.last = buf.start; buf.end = buf.last + NGX_CONF_BUFFER; buf.temporary = 1; /* 復制文件屬性及文件內容 */ cf->conf_file->file.fd = fd; cf->conf_file->file.name.len = filename->len; cf->conf_file->file.name.data = filename->data; cf->conf_file->file.offset = 0; cf->conf_file->file.log = cf->log; cf->conf_file->line = 1; type = parse_file; /* 解析的類型是配置文件 */ } else if (cf->conf_file->file.fd != NGX_INVALID_FILE) { type = parse_block; /* 解析的類型是block塊 */ } else { type = parse_param; /* 解析的類型是命令行配置 */ } for ( ;; ) { /* 語法分析函數 */ rc = ngx_conf_read_token(cf); /* * ngx_conf_read_token() may return * * NGX_ERROR there is error * NGX_OK the token terminated by ";" was found * NGX_CONF_BLOCK_START the token terminated by "{" was found * NGX_CONF_BLOCK_DONE the "}" was found * NGX_CONF_FILE_DONE the configuration file is done */ if (rc == NGX_ERROR) { goto done; } /* 解析block塊設置 */ if (rc == NGX_CONF_BLOCK_DONE) { if (type != parse_block) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\""); goto failed; } goto done; } /* 解析配置文件 */ if (rc == NGX_CONF_FILE_DONE) { if (type == parse_block) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected end of file, expecting \"}\""); goto failed; } goto done; } if (rc == NGX_CONF_BLOCK_START) { if (type == parse_param) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "block directives are not supported " "in -g option"); goto failed; } } /* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */ /* 自定義指令處理函數 */ if (cf->handler) { /* * the custom handler, i.e., that is used in the http's * "types { ... }" directive */ if (rc == NGX_CONF_BLOCK_START) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"{\""); goto failed; } /* 命令行配置處理函數 */ rv = (*cf->handler)(cf, NULL, cf->handler_conf); if (rv == NGX_CONF_OK) { continue; } if (rv == NGX_CONF_ERROR) { goto failed; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv); goto failed; } /* 若自定義指令處理函數handler為NULL,則調用Nginx內建的指令解析機制 */ rc = ngx_conf_handler(cf, rc); if (rc == NGX_ERROR) { goto failed; } } failed: rc = NGX_ERROR; done: if (filename) {/* 若是配置文件 */ if (cf->conf_file->buffer->start) { ngx_free(cf->conf_file->buffer->start); } if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, ngx_close_file_n " %s failed", filename->data); return NGX_CONF_ERROR; } cf->conf_file = prev; } if (rc == NGX_ERROR) { return NGX_CONF_ERROR; } return NGX_CONF_OK; } ~~~ ? ? ? 從配置解析函數的源碼可以看出,該函數分為兩個階段:**語法分析**和 **指令解析**。語法分析由 ngx_conf_read_token()函數完成。指令解析有兩種方式:一種是Nginx 內建的指令解析機制;另一種是自定義的指令解析機制。自定義指令解析源碼如下所示: ~~~ /* 自定義指令處理函數 */ if (cf->handler) { /* * the custom handler, i.e., that is used in the http's * "types { ... }" directive */ if (rc == NGX_CONF_BLOCK_START) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"{\""); goto failed; } /* 命令行配置處理函數 */ rv = (*cf->handler)(cf, NULL, cf->handler_conf); if (rv == NGX_CONF_OK) { continue; } if (rv == NGX_CONF_ERROR) { goto failed; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv); goto failed; } ~~~ ? ? ? 而Nginx 內置解析機制有函數ngx_conf_handler() 實現。其定義如下: ~~~ /* Nginx內建的指令解析機制 */ static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last) { char *rv; void *conf, **confp; ngx_uint_t i, found; ngx_str_t *name; ngx_command_t *cmd; name = cf->args->elts; found = 0; for (i = 0; ngx_modules[i]; i++) { cmd = ngx_modules[i]->commands; if (cmd == NULL) { continue; } for ( /* void */ ; cmd->name.len; cmd++) { if (name->len != cmd->name.len) { continue; } if (ngx_strcmp(name->data, cmd->name.data) != 0) { continue; } found = 1; /* * 只處理模塊類型為NGX_CONF_MODULE 或是當前正在處理的模塊類型; */ if (ngx_modules[i]->type != NGX_CONF_MODULE && ngx_modules[i]->type != cf->module_type) { continue; } /* is the directive's location right ? */ if (!(cmd->type & cf->cmd_type)) { continue; } /* 非block塊指令必須以";"分號結尾,否則出錯返回 */ if (!(cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "directive \"%s\" is not terminated by \";\"", name->data); return NGX_ERROR; } /* block塊指令必須后接"{"大括號,否則出粗返回 */ if ((cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "directive \"%s\" has no opening \"{\"", name->data); return NGX_ERROR; } /* is the directive's argument count right ? */ /* 驗證指令參數個數是否正確 */ if (!(cmd->type & NGX_CONF_ANY)) { /* 指令攜帶的參數只能是 1 個,且其參數值只能是 on 或 off */ if (cmd->type & NGX_CONF_FLAG) { if (cf->args->nelts != 2) { goto invalid; } } else if (cmd->type & NGX_CONF_1MORE) {/* 指令攜帶的參數必須超過 1 個 */ if (cf->args->nelts < 2) { goto invalid; } } else if (cmd->type & NGX_CONF_2MORE) {/* 指令攜帶的參數必須超過 2 個 */ if (cf->args->nelts < 3) { goto invalid; } } else if (cf->args->nelts > NGX_CONF_MAX_ARGS) { goto invalid; } else if (!(cmd->type & argument_number[cf->args->nelts - 1])) { goto invalid; } } /* set up the directive's configuration context */ conf = NULL; if (cmd->type & NGX_DIRECT_CONF) {/* 在core模塊使用 */ conf = ((void **) cf->ctx)[ngx_modules[i]->index]; } else if (cmd->type & NGX_MAIN_CONF) {/* 指令配置項出現在全局配置中,不屬于任何{}配置塊 */ conf = &(((void **) cf->ctx)[ngx_modules[i]->index]); } else if (cf->ctx) {/* 除了core模塊,其他模塊都是用該項 */ confp = *(void **) ((char *) cf->ctx + cmd->conf); if (confp) { conf = confp[ngx_modules[i]->ctx_index]; } } /* 執行指令解析回調函數 */ rv = cmd->set(cf, cmd, conf); if (rv == NGX_CONF_OK) { return NGX_OK; } if (rv == NGX_CONF_ERROR) { return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%s\" directive %s", name->data, rv); return NGX_ERROR; } } if (found) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%s\" directive is not allowed here", name->data); return NGX_ERROR; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unknown directive \"%s\"", name->data); return NGX_ERROR; invalid: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid number of arguments in \"%s\" directive", name->data); return NGX_ERROR; } ~~~ ### HTTP 模塊配置解析   這里主要是結構體 *ngx_command_t* ,我們在文章 《[Nginx 模塊開發](http://blog.csdn.net/chenhanzhun/article/details/42528951)》 對該結構體作了介紹,其定義如下: ~~~ struct ngx_command_s { /* 配置項名稱 */ ngx_str_t name; /* 配置項類型,type將指定配置項可以出現的位置以及攜帶參數的個數 */ ngx_uint_t type; /* 處理配置項的參數 */ char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); /* 在配置文件中的偏移量,conf與offset配合使用 */ ngx_uint_t conf; ngx_uint_t offset; /* 配置項讀取后的處理方法,必須指向ngx_conf_post_t 結構 */ void *post; }; ~~~ ? ? ? 若在上面的通用配置解析中,定義了如下的 http 配置項結構,則回調用http 配置項,并對該http 配置項進行解析。此時,解析的是http block?塊設置。 ~~~ static ngx_command_t ngx_http_commands[] = { { ngx_string("http"), NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, ngx_http_block, 0, 0, NULL }, ngx_null_command }; ~~~ ? ? ? http 是作為一個 core 模塊被 nginx 通用解析過程解析的,其核心就是http{} 塊指令回調,它完成了http 解析的整個功能,從初始化到計算配置結果。http{} 塊指令的流程是: - 創建并初始化上下文結構; - 調用通用模塊配置解析流程解析; - 根據解析結果進行配置項合并處理; ### 創建并初始化上下文結構   當 Nginx 檢查到 http{…} 配置項時,HTTP 配置模型就會啟動,則會建立一個*ngx_http_conf_ctx_t* 結構,該結構定義在文件中:[src/http/ngx_http_config.h](http://lxr.nginx.org/source/src/core/ngx_conf_file.h) ~~~ typedef struct{   /* 指針數組,數組中的每個元素指向所有 HTTP 模塊 create_main_conf 方法產生的結構體 */ void **main_conf; /* 指針數組,數組中的每個元素指向所有 HTTP 模塊 create_srv_conf 方法產生的結構體 */ void **srv_conf; /* 指針數組,數組中的每個元素指向所有 HTTP 模塊 create_loc_conf 方法產生的結構體 */ void **loc_conf; }ngx_http_conf_ctx_t; ~~~   此時,HTTP 框架為所有 HTTP 模塊建立 3 個數組,分別存放所有 HTTP 模塊的*create_main_conf*、*create_srv_conf* 、*create_loc_conf* 方法返回的地址指針。*ngx_http_conf_ctx_t* 結構的三個成員分別指向這 3 個數組。例如下面的例子是設置?*create_main_conf*、*create_srv_conf*?、*create_loc_conf*??返回的地址。 ~~~ ngx_http_conf_ctx *ctx; /* HTTP 框架生成 1 個 ngx_http_conf_ctx_t 結構變量 */ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); *(ngx_http_conf_ctx_t **) conf = ctx; ... /* 分別生成 3 個數組存儲所有的 HTTP 模塊的 create_main_conf、create_srv_conf、create_loc_conf 方法返回的地址 */ ctx->main_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module); /* 遍歷所有 HTTP 模塊 */ for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_HTTP_MODULE) { continue; } module = ngx_modules[m]->ctx; mi = ngx_modules[m]->ctx_index; /* 若實現了create_main_conf 方法,則調用該方法,并把返回的地址存儲到 main_conf 中 */ if (module->create_main_conf) { ctx->main_conf[mi] = module->create_main_conf(cf); } /* 若實現了create_srv_conf 方法,則調用該方法,并把返回的地址存儲到 srv_conf 中 */ if (module->create_srv_conf) { ctx->srv_conf[mi] = module->create_srv_conf(cf); } /* 若實現了create_loc_conf 方法,則調用該方法,并把返回的地址存儲到 loc_conf 中 */ if (module->create_loc_conf) { ctx->loc_conf[mi] = module->create_loc_conf(cf); } } pcf = *cf; cf->ctx = ctx; for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_HTTP_MODULE) { continue; } module = ngx_modules[m]->ctx; if (module->preconfiguration) { if (module->preconfiguration(cf) != NGX_OK) { return NGX_CONF_ERROR; } } } ~~~ ### 調用通用模塊配置解析流程解析 ? ? ? 從源碼?[src/http/ngx_http.c](http://lxr.nginx.org/source/src/http/ngx_http.c)?中可以看到,http 塊的配置解析是調用通用模塊的配置解析函數,其實現如下: ~~~ /* 調用通用模塊配置解析 */ /* parse inside the http{} block */ cf->module_type = NGX_HTTP_MODULE; cf->cmd_type = NGX_HTTP_MAIN_CONF; rv = ngx_conf_parse(cf, NULL); if (rv != NGX_CONF_OK) { goto failed; } ~~~ ### 根據解析結果進行配置項合并處理 ~~~ /* 根據解析結構進行合并處理 */ /* * init http{} main_conf's, merge the server{}s' srv_conf's * and its location{}s' loc_conf's */ cmcf = ctx->main_conf[ngx_http_core_module.ctx_index]; cscfp = cmcf->servers.elts; for (m = 0; ngx_modules[m]; m++) { if (ngx_modules[m]->type != NGX_HTTP_MODULE) { continue; } module = ngx_modules[m]->ctx; mi = ngx_modules[m]->ctx_index; /* init http{} main_conf's */ if (module->init_main_conf) { rv = module->init_main_conf(cf, ctx->main_conf[mi]); if (rv != NGX_CONF_OK) { goto failed; } } rv = ngx_http_merge_servers(cf, cmcf, module, mi); if (rv != NGX_CONF_OK) { goto failed; } } /* create location trees */ for (s = 0; s < cmcf->servers.nelts; s++) { clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index]; if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) { return NGX_CONF_ERROR; } if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) { return NGX_CONF_ERROR; } } if (ngx_http_init_phases(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } 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; } } } if (ngx_http_variables_init_vars(cf) != NGX_OK) { return NGX_CONF_ERROR; } /* * http{}'s cf->ctx was needed while the configuration merging * and in postconfiguration process */ *cf = pcf; if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) { return NGX_CONF_ERROR; } /* optimize the lists of ports, addresses and server names */ if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) { return NGX_CONF_ERROR; } return NGX_CONF_OK; failed: *cf = pcf; return rv; ~~~ ### HTTP 配置解析流程 ? ? ? 從上面的分析中可以總結出 HTTP 配置解析的流程如下: - Nginx 進程進入主循環,在主循環中調用配置解析器解析配置文件*nginx.conf*; - 在配置文件中遇到 http{} 塊配置,則 HTTP 框架開始初始化并啟動,其由函數 ngx_http_block() 實現; - HTTP 框架初始化所有 HTTP 模塊的序列號,并創建 3 個類型為?*ngx_http_conf_ctx_t?*結構的數組用于存儲所有HTTP 模塊的*create_main_conf*、*create_srv_conf*、*create_loc_conf*方法返回的指針地址; - 調用每個 HTTP 模塊的 preconfiguration 方法; - HTTP 框架調用函數 ngx_conf_parse() 開始循環解析配置文件?*nginx.conf?*中的http{}塊里面的所有配置項; - HTTP 框架處理完畢 http{} 配置項,根據解析配置項的結果,必要時進行配置項合并處理; - 繼續處理其他 http{} 塊之外的配置項,直到配置文件解析器處理完所有配置項后通知Nginx 主循環配置項解析完畢。此時,Nginx 才會啟動Web 服務器; ### 合并配置項 ? ? ? HTTP 框架解析完畢 http{} 塊配置項時,會根據解析的結果進行合并配置項操作,即合并 http{}、server{}、location{} 不同塊下各HTTP 模塊生成的存放配置項的結構體。其合并過程如下所示: - 若 HTTP 模塊實現了 *merge_srv_conf* 方法,則將 http{} 塊下*create_srv_conf* 生成的結構體與遍歷每一個 server{}配置塊下的結構體進行*merge_srv_conf* 操作; - 若 HTTP 模塊實現了 *merge_loc_conf* 方法,則將 http{} 塊下*create_loc_conf* 生成的結構體與嵌套每一個server{} 配置塊下的結構體進行*merge_loc_conf* 操作; - 若 HTTP 模塊實現了 *merge_loc_conf* 方法,則將server{} 塊下*create_loc_conf* 生成的結構體與嵌套每一個location{}配置塊下的結構體進行*merge_loc_conf* 操作; - 若 HTTP 模塊實現了 *merge_loc_conf* 方法,則將location{} 塊下*create_loc_conf* 生成的結構體與嵌套每一個location{}配置塊下的結構體進行*merge_loc_conf* 操作; ? ? ? 以下是合并配置項操作的源碼實現: ~~~ /* 合并配置項操作 */ static char * ngx_http_merge_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, ngx_http_module_t *module, ngx_uint_t ctx_index) { char *rv; ngx_uint_t s; ngx_http_conf_ctx_t *ctx, saved; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t **cscfp; cscfp = cmcf->servers.elts; ctx = (ngx_http_conf_ctx_t *) cf->ctx; saved = *ctx; rv = NGX_CONF_OK; /* 遍歷每一個server{}塊 */ for (s = 0; s < cmcf->servers.nelts; s++) { /* merge the server{}s' srv_conf's */ ctx->srv_conf = cscfp[s]->ctx->srv_conf; /* * 若定義了merge_srv_conf 方法; * 則進行http{}塊下create_srv_conf 生成的結構體與遍歷server{}塊配置項生成的結構體進行merge_srv_conf操作; */ if (module->merge_srv_conf) { rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index], cscfp[s]->ctx->srv_conf[ctx_index]); if (rv != NGX_CONF_OK) { goto failed; } } /* * 若定義了merge_loc_conf 方法; * 則進行http{}塊下create_loc_conf 生成的結構體與嵌套server{}塊配置項生成的結構體進行merge_loc_conf操作; */ if (module->merge_loc_conf) { /* merge the server{}'s loc_conf */ ctx->loc_conf = cscfp[s]->ctx->loc_conf; rv = module->merge_loc_conf(cf, saved.loc_conf[ctx_index], cscfp[s]->ctx->loc_conf[ctx_index]); if (rv != NGX_CONF_OK) { goto failed; } /* merge the locations{}' loc_conf's */ /* * 若定義了merge_loc_conf 方法; * 則進行server{}塊下create_loc_conf 生成的結構體與嵌套location{}塊配置項生成的結構體進行merge_loc_conf操作; */ clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index]; rv = ngx_http_merge_locations(cf, clcf->locations, cscfp[s]->ctx->loc_conf, module, ctx_index); if (rv != NGX_CONF_OK) { goto failed; } } } failed: *ctx = saved; return rv; } static char * ngx_http_merge_locations(ngx_conf_t *cf, ngx_queue_t *locations, void **loc_conf, ngx_http_module_t *module, ngx_uint_t ctx_index) { char *rv; ngx_queue_t *q; ngx_http_conf_ctx_t *ctx, saved; ngx_http_core_loc_conf_t *clcf; ngx_http_location_queue_t *lq; if (locations == NULL) { return NGX_CONF_OK; } ctx = (ngx_http_conf_ctx_t *) cf->ctx; saved = *ctx; /* * 若定義了merge_loc_conf 方法; * 則進行location{}塊下create_loc_conf 生成的結構體與嵌套location{}塊配置項生成的結構體進行merge_loc_conf操作; */ for (q = ngx_queue_head(locations); q != ngx_queue_sentinel(locations); q = ngx_queue_next(q)) { lq = (ngx_http_location_queue_t *) q; clcf = lq->exact ? lq->exact : lq->inclusive; ctx->loc_conf = clcf->loc_conf; rv = module->merge_loc_conf(cf, loc_conf[ctx_index], clcf->loc_conf[ctx_index]); if (rv != NGX_CONF_OK) { return rv; } /* * 遞歸調用該函數; * 因為location{}繼續內嵌location{} */ rv = ngx_http_merge_locations(cf, clcf->locations, clcf->loc_conf, module, ctx_index); if (rv != NGX_CONF_OK) { return rv; } } *ctx = saved; return NGX_CONF_OK; } ~~~ ### 處理自定義的配置 ? ? ? 在文章中 《[Nginx 模塊開發](http://blog.csdn.net/chenhanzhun/article/details/42528951)》,我們給出了“Hello World” 的開發例子,在這個開發例子中,我們定義了自己的配置項,配置項名稱的結構體定義如下: ~~~ typedef struct { ngx_str_t hello_string; ngx_int_t hello_counter; }ngx_http_hello_loc_conf_t; ~~~ ? ? ? 為了處理我們定義的配置項結構,因此,我們把 *ngx_command_t* 結構體定義如下: ~~~ static ngx_command_t ngx_http_hello_commands[] = { { ngx_string("hello_string"), NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1, ngx_http_hello_string, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_hello_loc_conf_t, hello_string), NULL }, { ngx_string("hello_counter"), NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_http_hello_counter, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_hello_loc_conf_t, hello_counter), NULL }, ngx_null_command }; ~~~ ? ? ? 處理方法 *ngx_http_hello_string* 和*ngx_http_hello_counter* 定義如下: ~~~ static char * ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_hello_loc_conf_t* local_conf; local_conf = conf; char* rv = ngx_conf_set_str_slot(cf, cmd, conf); ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "hello_string:%s", local_conf->hello_string.data); return rv; } static char *ngx_http_hello_counter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_hello_loc_conf_t* local_conf; local_conf = conf; char* rv = NULL; rv = ngx_conf_set_flag_slot(cf, cmd, conf); ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "hello_counter:%d", local_conf->hello_counter); return rv; } ~~~ ### error 日志 ? ? ? Nginx 日志模塊為其他模塊提供了基本的日志記錄功能,日志模塊定義如下:[src/?core/?ngx_log.c](http://lxr.nginx.org/source/src/core/ngx_log.c) ~~~ static ngx_command_t ngx_errlog_commands[] = { {ngx_string("error_log"), NGX_MAIN_CONF|NGX_CONF_1MORE, ngx_error_log, 0, 0, NULL}, ngx_null_command }; static ngx_core_module_t ngx_errlog_module_ctx = { ngx_string("errlog"), NULL, NULL }; ngx_module_t ngx_errlog_module = { NGX_MODULE_V1, &ngx_errlog_module_ctx, /* module context */ ngx_errlog_commands, /* module directives */ NGX_CORE_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; ~~~ ? ? ? Nginx 日志模塊對于支持可變參數提供了三個接口,這三個接口定義在文件:[src/?core/?ngx_log.h](http://lxr.nginx.org/source/src/core/ngx_log.h) ~~~ #define ngx_log_error(level, log, ...) \ if ((log)->log_level >= level) ngx_log_error_core(level, log, __VA_ARGS__) void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, const char *fmt, ...); #define ngx_log_debug(level, log, ...) \ if ((log)->log_level & level) \ ngx_log_error_core(NGX_LOG_DEBUG, log, __VA_ARGS__) ~~~ ? ? ? Nginx 日志模塊記錄日志的核心功能是由ngx_log_error_core 方法實現,ngx_log_error 和ngx_log_debug 宏定義只是對其進行簡單的封裝,一般情況下日志調用只需要這兩個宏定義。 ? ? ? ngx_log_error 和 ngx_log_debug 宏定義都包括參數 level、log、err、fmt,下面分別對這些參數進行簡單的介紹: **level 參數**:對于 ngx_log_error 宏來說,level 表示當前日志的級別,其取值如下所示: ~~~ /* ngx_log_error中level參數的取值;下面 9 個日志的級別依次從高到低 */ #define NGX_LOG_STDERR 0 /* 最高級別日志,將日志輸出到標準錯誤設備 */ #define NGX_LOG_EMERG 1 #define NGX_LOG_ALERT 2 #define NGX_LOG_CRIT 3 #define NGX_LOG_ERR 4 #define NGX_LOG_WARN 5 #define NGX_LOG_NOTICE 6 #define NGX_LOG_INFO 7 #define NGX_LOG_DEBUG 8 /* 最低級別日志,屬于調試級別 */ ~~~ ? ? ? 使用 ngx_log_error 宏記錄日志時,若傳入的level 級別小于或等于log 參數中的日志級別,就會輸出日志內容,否則忽略該日志。 ? ? ? 在使用 ngx_log_debug 宏時,參數level 不同于ngx_log_error 宏的level 參數,它表達的不是日志級別,而是日志類型。ngx_log_debug 宏記錄日志時必須是NGX_LOG_DEBUG 調試級別,這里的level 取值如下: ~~~ /* ngx_log_debug中level參數的取值 */ #define NGX_LOG_DEBUG_CORE 0x010 /* nginx核心模塊的調試日志 */ #define NGX_LOG_DEBUG_ALLOC 0x020 /* nginx在分配內存時使用的調試日志 */ #define NGX_LOG_DEBUG_MUTEX 0x040 /* nginx在使用進程鎖時使用的調試日志 */ #define NGX_LOG_DEBUG_EVENT 0x080 /* nginx event模塊的調試日志 */ #define NGX_LOG_DEBUG_HTTP 0x100 /* nginx http模塊的調試日志 */ #define NGX_LOG_DEBUG_MAIL 0x200 /* nginx mail模塊的調試日志 */ #define NGX_LOG_DEBUG_MYSQL 0x400 /* 與MySQL相關的nginx模塊所使用的調試日志 */ ~~~ ? ? ? 當 HTTP 模塊調用ngx_log_debug 宏記錄日志時,傳入的level 參數是NGX_LOG_DEBUG_HTTP,此時,若log 參數不屬于HTTP 模塊,若使用event 事件模塊的log,則不會輸出任何日志。 **log 參數**:log 參數的結構定義如下:[src/core/ngx_log.h](http://lxr.nginx.org/source/src/core/ngx_log.h);從其結構中可以知道,若只想把相應的信息記錄到日志文件中,則不需要關系參數?log?的構造。 ~~~ /* ngx_log_t 結構的定義 */ struct ngx_log_s { /* 日志級別或日志類型 */ ngx_uint_t log_level; /* 日志文件 */ ngx_open_file_t *file; /* 連接數,不為0時會輸出到日志文件中 */ ngx_atomic_uint_t connection; /* 記錄日志時的回調方法,不是DEBUG調試級別才會被調用 */ ngx_log_handler_pt handler; /* 模塊的data */ void *data; /* * we declare "action" as "char *" because the actions are usually * the static strings and in the "u_char *" case we have to override * their types all the time */ char *action; /* 指向日志鏈表的下一個日志 */ ngx_log_t *next; }; ~~~ **err 參數**:err 參數是錯誤編碼,一般是執行系統調用失敗后取得的errno 參數。當err 不為 0 時,Nginx 日志模塊將會在正常日志輸出這個錯誤編碼以及其對應的字符串形成的錯誤信息。 **fmt 參數**:fmt 參數類似于C 語言中的printf 函數的輸出格式。 參考資料: 《深入理解 Nginx 》 《[nginx 啟動階段](http://tengine.taobao.org/book/chapter_11.html)》 《Nginx高性能Web服務器詳解》
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看