前一篇文章介绍了nginx的启动初始化过程,包括了所有模块的初始化过程,比如http模块、事件模块等。这里再详细介绍一下http模块的启动过程,还记得在前一篇文章中提到过ngx_conf_parse函数背后隐藏了大量的细节吗?这里就揭开这层神秘的面纱,去看看几个重要的http模块是如何初始化的。这里依然沿用上一篇文章的结构,首先来看几个重要的数据结构。
1. 重要的数据结构
1. ngx_http_module_t
所有http模块都是ngx_http_module_t类型,所有的属性都是回调函数,在http模块初始化过程的不同阶段调用。当一个指令既允许出现在main块(在http{}块内,但是在server{}块外的区域)、server块(在server{}块内,但是在location{}块外的区域)、location块内时,就需要对这些指令进行继承和覆盖的处理,由merge_src_conf和merge_loc_conf完成。
typedef struct {
/**
* 在解析配置文件中http{}配置块前调用
*/
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
/**
* 在解析配置文件中http{}配置块后调用
*/
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
/**
* 创建http模块的main config
*/
void *(*create_main_conf)(ngx_conf_t *cf);
/**
* 初始化http模块的main config
*/
char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
/**
* 创建http模块的server config
*/
void *(*create_srv_conf)(ngx_conf_t *cf);
/**
* 合并http模块的server config,用于实现server config到main config的指令的继承、覆盖
*/
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
/**
* 创建http模块的location config
*/
void *(*create_loc_conf)(ngx_conf_t *cf);
/**
* 合并http模块的location config,用于实现location config到server config的指令的继承、覆盖
*/
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
2. ngx_http_conf_ctx_t
此结构用于保存所有http模块的main、server和location的config结构。可以看到ngx_http_conf_ctx_t的三个属性就是三个数组,数组大小由ngx_http_max_module指定的,这个变量在指令http块的回调函数时初始化。还记得在上一篇介绍的ngx_module_t结构的ctx_index属性吗?对于一个http模块,ctx_index属性就指示了该模块的main、srv和loc的config结构在这三个数组中的下标。
typedef struct {
/**
* 所有模块的main config数组
*/
void **main_conf;
/**
* 所有模块的server config数组
*/
void **srv_conf;
/**
* 所有模块的location config数组
*/
void **loc_conf;
} ngx_http_conf_ctx_t;
在我们编写http模块时,经常要获取模块的main、srv和loc配置信息,nginx提供了两类宏来处理:
在处理请求时,可以通过:
#define ngx_http_get_module_main_conf(r, module) \
(r)->main_conf[module.ctx_index]
#define ngx_http_get_module_srv_conf(r, module) (r)->srv_conf[module.ctx_index]
#define ngx_http_get_module_loc_conf(r, module) (r)->loc_conf[module.ctx_index]
实现起来很简单只要传入的r是ngx_http_request_t类型的,module就是对应的模块,直接通过ctx_index索引就可以获取到对应配置结构。
在解析配置文件过程,通常用在指令的set回调函数中:
#define ngx_http_conf_get_module_main_conf(cf, module) \
((ngx_http_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]
#define ngx_http_conf_get_module_srv_conf(cf, module) \
((ngx_http_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]
#define ngx_http_conf_get_module_loc_conf(cf, module) \
((ngx_http_conf_ctx_t *) cf->ctx)->loc_conf[module.ctx_index]
cf是ngx_conf_t类型,module同样是对应的模块。
2. http模块的启动过程
Events Http
/ \
Server Server
/ \ \
Location Location Location
这棵树中,每个节点对应一个配置块,配置文件的解析就是对这棵树的深度优先遍历,并对每个节点调用ngx_conf_parse函数。其中的Http节点对应的就是ngx_http_module模块,是由它驱动后面的server块和location块的解析,其他的细节部分会在nginx配置文件解析中讲解。下面先来看一下ngx_http_module的结构:
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
};
static ngx_core_module_t ngx_http_module_ctx = {
ngx_string("http"),
NULL,
NULL
};
ngx_module_t ngx_http_module = {
NGX_MODULE_V1,
&ngx_http_module_ctx, /* module context */
ngx_http_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
};
ngx_http_module模块只有一个http指令,也就是http{},这个指令的回调函数是ngx_http_block,而且它的类型中有NGX_CONF_BLOCK,所以这个模块会在解析所有core module时,在配置文件中遇到http{}块时被调用。也就是说http模块的初始化的入口就是ngx_http_block,下面具体看一下这个函数