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框架模块配置的内存布局: