http子產品是nginx中數量最多,也是配置最複雜的子產品。http子產品的配置最多可以分為三級,分别是http級别,server級别和location級别。nginx為http子產品定義了一組接口,用于子產品建立,設定,合并各級别的配置項。在配置讀取階段一共有9個接口,在不同階段,不同位置被調用。其中8個在ngx_http_module_t中定義,這8個回調是子產品級别的,還有1個是指令級别的,由子產品的指令定義。ngx_module_t定義如下所示:
typedef struct {
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);//準備讀取配置文本前
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);//讀取并解析配置結束後
void *(*create_main_conf)(ngx_conf_t *cf);//建立main級别上下文
char *(*init_main_conf)(ngx_conf_t *cf, void *conf);//初始化main級别上下文
void *(*create_srv_conf)(ngx_conf_t *cf);//建立server級别配置上下文
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);//合并server級别配置上下文
void *(*create_loc_conf)(ngx_conf_t *cf);//建立location級别配置上下文
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);//合并location級别配置上下文
} ngx_http_module_t;
nginx在解析配置檔案的過程,會依次調用這些函數,它們的調用順序如下:
1. create_main_conf
2. create_srv_conf
3. create_loc_conf
4. preconfiguration
5. 讀取配置後執行指令的set回調
6. init_main_conf
7. merge_srv_conf
8. merge_loc_conf
9. postconfiguration
每個http子產品可以根據自己的需要實作對應的接口,http架構會在合适的位置調用這些接口。
前面介紹的nginx的配置的記憶體布局一問中說過,cycle的conf_ctx成員儲存的是各NGX_CORE_MODULE類型的子產品的配置位址。與http密切相關的NGX_CORE_MODULE類型的子產品是ngx_http_module,它的配置的實際類型是ngx_http_conf_ctx_t *。ngx_http_module子產品隻有一個指令,就是"http",如下所示:(關于ngx_http_module和http指令的更詳細的分析參考這篇文章)
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塊指令是nginx配置檔案中進行http相關配置的唯一入口,當nginx遇到http塊指令時,就會建立一個ngx_http_conf_ctx_t結構體用來儲存http塊内的配置,如下代碼所示:
static char *
ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *rv;
ngx_uint_t mi, m, s;
ngx_conf_t pcf;
ngx_http_module_t *module;
ngx_http_conf_ctx_t *ctx;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t **cscfp;
ngx_http_core_main_conf_t *cmcf;
if (*(ngx_http_conf_ctx_t **) conf) {
return "is duplicate";
}
/* the main http context */
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
*(ngx_http_conf_ctx_t **) conf = ctx;
/* count the number of the http modules and set up their indices */
ngx_http_max_module = ngx_count_modules(cf->cycle, NGX_HTTP_MODULE);
/* the http main_conf context, it is the same in the all http contexts */
ctx->main_conf = ngx_pcalloc(cf->pool,
sizeof(void *) * ngx_http_max_module);
if (ctx->main_conf == NULL) {
return NGX_CONF_ERROR;
}
/*
* the http null srv_conf context, it is used to merge
* the server{}s' srv_conf's
*/
ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->srv_conf == NULL) {
return NGX_CONF_ERROR;
}
/*
* the http null loc_conf context, it is used to merge
* the server{}s' loc_conf's
*/
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->loc_conf == NULL) {
return NGX_CONF_ERROR;
}
...
}
另外,http塊下的server塊和location塊的配置也是使用ngx_http_conf_ctx_t作為配置的載體,如下代碼所示(ngx_http_core_server是遇到server指令時執行的回調,而ngx_http_core_location是遇到location指令時執行的回調):
static char *
ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
char *rv;
void *mconf;
ngx_uint_t i;
ngx_conf_t pcf;
ngx_http_module_t *module;
struct sockaddr_in *sin;
ngx_http_conf_ctx_t *ctx, *http_ctx;
ngx_http_listen_opt_t lsopt;
ngx_http_core_srv_conf_t *cscf, **cscfp;
ngx_http_core_main_conf_t *cmcf;
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
http_ctx = cf->ctx;
ctx->main_conf = http_ctx->main_conf;
/* the server{}'s srv_conf */
ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->srv_conf == NULL) {
return NGX_CONF_ERROR;
}
/* the server{}'s loc_conf */
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->loc_conf == NULL) {
return NGX_CONF_ERROR;
}
...
}
static char *
ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
char *rv;
u_char *mod;
size_t len;
ngx_str_t *value, *name;
ngx_uint_t i;
ngx_conf_t save;
ngx_http_module_t *module;
ngx_http_conf_ctx_t *ctx, *pctx;
ngx_http_core_loc_conf_t *clcf, *pclcf;
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
pctx = cf->ctx;
ctx->main_conf = pctx->main_conf;
ctx->srv_conf = pctx->srv_conf;
ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->loc_conf == NULL) {
return NGX_CONF_ERROR;
}
...
}
也就是說,當碰到server塊指令和location塊指令的時候,nginx也會建立一個ngx_http_conf_ctx_t結構體用來儲存塊内的配置。為什麼三個級别的配置都使用這個結構體來儲存呢,它們之間有什麼關系?先看ngx_http_conf_ctx_t的定義。 ngx_http_conf_ctx_t 有三個成員main_conf,srv_conf和loc_conf,如下所示:
typedef struct {
void **main_conf;
void **srv_conf;
void **loc_conf;
} ngx_http_conf_ctx_t;
我們先分析main_conf成員。我們先從http塊指令配置設定的ngx_http_conf_ctx_t的main_conf開始。它指向一個數組,數組大小與nginx中http子產品(module_type為NGX_HTTP_MODULE的子產品)的數量一緻,數組元素是各http子產品在main級别的配置的位址(由子產品的create_main_conf傳回),http子產品的ctx_index字段就是它們在這個數組中的下标。main_conf數組的第一個元素,也就是ctx_index為0的元素是ngx_http_core_module的main級别的配置位址。ngx_http_core_module是http最核心的子產品之一,實際上它和ngx_http_module構成了http的架構,它不僅實作了很多http塊内的重要指令,例如listen,server,location等,它所儲存的main,srv,location級别的配置也是nginx的http架構運作所依賴的核心資料(為了避免篇幅過長,ngx_http_core_module的分析會在後續單獨開篇講述)。ngx_http_core_module的main級别配置的實際類型是ngx_http_core_main_conf_t,如下所示:
typedef struct {
ngx_array_t servers; /* ngx_http_core_srv_conf_t */
ngx_http_phase_engine_t phase_engine;
ngx_hash_t headers_in_hash;
ngx_hash_t variables_hash;
ngx_array_t variables; /* ngx_http_variable_t */
ngx_array_t prefix_variables; /* ngx_http_variable_t */
ngx_uint_t ncaptures;
ngx_uint_t server_names_hash_max_size;
ngx_uint_t server_names_hash_bucket_size;
ngx_uint_t variables_hash_max_size;
ngx_uint_t variables_hash_bucket_size;
ngx_hash_keys_arrays_t *variables_keys;
ngx_array_t *ports;
ngx_uint_t try_files; /* unsigned try_files:1 */
ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];
} ngx_http_core_main_conf_t;
可以看到,ngx_http_core_main_conf_t儲存的都是全局的配置,比如server資訊,http變量,http請求頭部,http架構等配置,這裡我們隻關注servers字段,因為它與http架構配置的記憶體布局有直接關系。servers是一個動态數組,它的元素是各server塊的server 級别配置的位址。每次http架構解析完一個server塊時,都會把該server塊的配置位址儲存在這個數組中。是以,http塊内有幾個server塊,servers的元素就有幾個。前面說到,當遇到server塊指令和location塊指令的時候,nginx也會建立一個ngx_http_conf_ctx_t來儲存配置,那麼,它們的main_conf成員儲存的又是什麼内容呢?實際上,它們的main_conf成員與http塊指令建立的ngx_http_conf_ctx_t的main_conf成員指向的是同一塊記憶體,即各http子產品在main級别的配置數組的首位址。這是因為http main級别的配置隻有一份,這份配置是全局通用的,把server和location級别的main_conf指向main級别的main_conf,可以讓server和location内的子產品也能通路到這些配置,又不浪費記憶體。
下面再分析srv_conf。依舊先從http塊指令建立的ngx_http_conf_ctx_t的srv_conf開始。它和main_conf一樣,也指向一個數組,數組大小與nginx中http子產品(module_type為NGX_HTTP_MODULE的子產品)的數量一緻,數組元素是各http子產品在server級别的配置的位址(由子產品的create_srv_conf傳回),http子產品的ctx_index字段就是它們在這個數組中的下标。數組的第一個元素是ngx_http_core_module在main級别的server配置的位址,配置的實際類型是ngx_http_core_srv_conf_t,如下所示:
typedef struct {
/* array of the ngx_http_server_name_t, "server_name" directive */
ngx_array_t server_names;
/* server ctx */
ngx_http_conf_ctx_t *ctx;
ngx_str_t server_name;
size_t connection_pool_size;
size_t request_pool_size;
size_t client_header_buffer_size;
ngx_bufs_t large_client_header_buffers;
ngx_msec_t client_header_timeout;
ngx_flag_t ignore_invalid_headers;
ngx_flag_t merge_slashes;
ngx_flag_t underscores_in_headers;
unsigned listen:1;
#if (NGX_PCRE)
unsigned captures:1;
#endif
ngx_http_core_loc_conf_t **named_locations;
} ngx_http_core_srv_conf_t;
那麼server塊和location塊指令建立的ngx_http_conf_t的srv_conf儲存的又是什麼呢?對于server塊來說,srv_conf儲存的是各http子產品在server級别的server配置的位址,上面說到的ngx_http_core_main_conf_t的servers成員儲存的就是各server塊的srv_conf的第一個元素的位址。main級别和server級别可以同時存在server配置,這樣http子產品可以自由選擇如何合并main級别和server級别的server配置(通過merge_srv_conf回調函數實作)。對于location塊來說,srv_conf儲存的則是其所屬的server級别的server配置的位址,因為在同一個server塊内,server相關的配置隻有一份,是以server塊内的所有location都共享這一份配置。
最後再來分析loc_conf。依舊先從http塊指令建立的ngx_http_conf_ctx_t的loc_conf開始。它與main_conf和srv_conf一樣,也指向一個數組,數組大小與nginx中http子產品(module_type為NGX_HTTP_MODULE的子產品)的數量一緻,數組元素是各http子產品在location級别的配置的位址(由子產品的create_loc_conf傳回),http子產品的ctx_index字段就是它們在這個數組中的下标。數組的第一個元素是ngx_http_core_module的main級别的location配置的位址。配置的實際類型是ngx_http_core_loc_conf_t,如下所示:
struct ngx_http_core_loc_conf_s {
ngx_str_t name; /* location name */
#if (NGX_PCRE)
ngx_http_regex_t *regex;
#endif
unsigned noname:1; /* "if () {}" block or limit_except */
unsigned lmt_excpt:1;
unsigned named:1;
unsigned exact_match:1;
unsigned noregex:1;
unsigned auto_redirect:1;
#if (NGX_HTTP_GZIP)
unsigned gzip_disable_msie6:2;
unsigned gzip_disable_degradation:2;
#endif
ngx_http_location_tree_node_t *static_locations;
#if (NGX_PCRE)
ngx_http_core_loc_conf_t **regex_locations;
#endif
/* pointer to the modules' loc_conf */
void **loc_conf;
uint32_t limit_except;
void **limit_except_loc_conf;
ngx_http_handler_pt handler;
/* location name length for inclusive location with inherited alias */
size_t alias;
ngx_str_t root; /* root, alias */
ngx_str_t post_action;
ngx_array_t *root_lengths;
ngx_array_t *root_values;
ngx_array_t *types;
ngx_hash_t types_hash;
ngx_str_t default_type;
off_t client_max_body_size; /* client_max_body_size */
off_t directio; /* directio */
off_t directio_alignment; /* directio_alignment */
size_t client_body_buffer_size; /* client_body_buffer_size */
size_t send_lowat; /* send_lowat */
size_t postpone_output; /* postpone_output */
size_t limit_rate; /* limit_rate */
size_t limit_rate_after; /* limit_rate_after */
size_t sendfile_max_chunk; /* sendfile_max_chunk */
size_t read_ahead; /* read_ahead */
ngx_msec_t client_body_timeout; /* client_body_timeout */
ngx_msec_t send_timeout; /* send_timeout */
ngx_msec_t keepalive_timeout; /* keepalive_timeout */
ngx_msec_t lingering_time; /* lingering_time */
ngx_msec_t lingering_timeout; /* lingering_timeout */
ngx_msec_t resolver_timeout; /* resolver_timeout */
ngx_resolver_t *resolver; /* resolver */
time_t keepalive_header; /* keepalive_timeout */
ngx_uint_t keepalive_requests; /* keepalive_requests */
ngx_uint_t keepalive_disable; /* keepalive_disable */
ngx_uint_t satisfy; /* satisfy */
ngx_uint_t lingering_close; /* lingering_close */
ngx_uint_t if_modified_since; /* if_modified_since */
ngx_uint_t max_ranges; /* max_ranges */
ngx_uint_t client_body_in_file_only; /* client_body_in_file_only */
ngx_flag_t client_body_in_single_buffer;
/* client_body_in_singe_buffer */
ngx_flag_t internal; /* internal */
ngx_flag_t sendfile; /* sendfile */
ngx_flag_t aio; /* aio */
ngx_flag_t aio_write; /* aio_write */
ngx_flag_t tcp_nopush; /* tcp_nopush */
ngx_flag_t tcp_nodelay; /* tcp_nodelay */
ngx_flag_t reset_timedout_connection; /* reset_timedout_connection */
ngx_flag_t absolute_redirect; /* absolute_redirect */
ngx_flag_t server_name_in_redirect; /* server_name_in_redirect */
ngx_flag_t port_in_redirect; /* port_in_redirect */
ngx_flag_t msie_padding; /* msie_padding */
ngx_flag_t msie_refresh; /* msie_refresh */
ngx_flag_t log_not_found; /* log_not_found */
ngx_flag_t log_subrequest; /* log_subrequest */
ngx_flag_t recursive_error_pages; /* recursive_error_pages */
ngx_uint_t server_tokens; /* server_tokens */
ngx_flag_t chunked_transfer_encoding; /* chunked_transfer_encoding */
ngx_flag_t etag; /* etag */
#if (NGX_HTTP_GZIP)
ngx_flag_t gzip_vary; /* gzip_vary */
ngx_uint_t gzip_http_version; /* gzip_http_version */
ngx_uint_t gzip_proxied; /* gzip_proxied */
#if (NGX_PCRE)
ngx_array_t *gzip_disable; /* gzip_disable */
#endif
#endif
#if (NGX_THREADS || NGX_COMPAT)
ngx_thread_pool_t *thread_pool;
ngx_http_complex_value_t *thread_pool_value;
#endif
#if (NGX_HAVE_OPENAT)
ngx_uint_t disable_symlinks; /* disable_symlinks */
ngx_http_complex_value_t *disable_symlinks_from;
#endif
ngx_array_t *error_pages; /* error_page */
ngx_http_try_file_t *try_files; /* try_files */
ngx_path_t *client_body_temp_path; /* client_body_temp_path */
ngx_open_file_cache_t *open_file_cache;
time_t open_file_cache_valid;
ngx_uint_t open_file_cache_min_uses;
ngx_flag_t open_file_cache_errors;
ngx_flag_t open_file_cache_events;
ngx_log_t *error_log;
ngx_uint_t types_hash_max_size;
ngx_uint_t types_hash_bucket_size;
ngx_queue_t *locations;
#if 0
ngx_http_core_loc_conf_t *prev_location;
#endif
};
再看server塊指令建立的ngx_http_conf_ctx_t的loc_conf成員,它也指向一個數組,數組大小與http子產品數量一緻,數組儲存的是各http子產品在server級别的location配置位址。數組的第一個元素是ngx_http_core_module在該server下的location級别的配置,類型也是ngx_http_core_loc_conf_t,其中的locations字段是一個雙向連結清單,把該server下的所有location配置串聯在一起。
最後,location塊指令建立的ngx_http_conf_ctx_t的loc_conf成員同樣也指向一個數組,數組大小與http子產品數量一緻,數組儲存的是各http子產品在location級别的location配置位址。數組的第一個元素是ngx_http_core_module在該location下的location界别的配置,類型也是ngx_http_core_loc_conf_t,其中的loc_conf字段指向該location所屬的server級别的loc_conf數組的位址。通過這樣方式,各http子產品可以自由選擇如何合并main,server和location級别的location配置(通過merge_loc_conf回調實作)。
下圖綜合展示了http架構子產品配置的記憶體布局:
