前面的nginx配置原理解析(一)我们重点阐述了nginx配置的起源,同时以http模块的配置为例勾勒了nginx配置数据结构在内存中的映像。本文继续(一)未完成的部分,仔细描述http块内的配置解析,同时为读者勾画整个解析完成后内存配置结构。
预备知识
nginx http模块的配置一般形式如下:
http {
test_str "hi"
...
server {
...
test_str "how"
location /hello {
...
}
location /hi {
...
test_str "how are u"
}
...
}
server {
location /x {
...
}
location /y {
...
}
}
}
http 块下面可以包含server块,而server块内也可嵌套location块,同时,每个模块的可以根据自己的需求将同一个配置同时出现在http、server、location块中,但是后续也必须得由自己决定,如上面示例的test_str。
nginx配置解析过程
在这里,我们简单描述下nginx配置解析过程以及函数调用关系。首先,nginx由许多模块构成,每个配置项应该是由其中的某个模块来解析,因此,整个模块解析过程其实就是“加载-发现-解析”的循环。
所有的模块配置解析都是由ngx_conf_parse()发起的。在这里先调用ngx_read_token()读取一个配置项,然后调用ngx_conf_handler()来解析配置项。以http模块为例,如遇到"http"这个指令时,会调用ngx_http_block()来解析整个http模块的配置。
关于ngx_http_block()函数初始化我们已经在(一)中仔细描述过了,并且给出了在内存中的映像图,今天我们就接着这个继续往下。
ngx_http_block
在(一)中我们讲到了ngx_http_block()过程的初始化,但是具体的解析我们未涉及。
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;
if (module->create_main_conf) {
ctx->main_conf[mi] = module->create_main_conf(cf);
if (ctx->main_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
if (module->create_srv_conf) {
ctx->srv_conf[mi] = module->create_srv_conf(cf);
if (ctx->srv_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
if (module->create_loc_conf) {
ctx->loc_conf[mi] = module->create_loc_conf(cf);
if (ctx->loc_conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
}
这里我们调用每个模块的三个回调函数(不一定需要全部实现),并在内存中创建其映像关系。之所以需要创建loc_conf、server_conf、main_conf是因为nginx的某个配置可能会同时出现在main、server、location范围,我们需要创建这些映像以便于合并。
当准备工作完成以后,会正式进入配置解析流程
/* 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;
}
还是调用ngx_conf_parse()来解析配置,只是此时配置环境的上下文已经发生改变,cf->module_type = NGX_HTTP_MODULE,cf->cmd_type = NGX_HTTP_MAIN_CONF。表示此时要解析的是http模块的main block。
ngx_http_core_server
我们接下来重点研究对"server"指令的解析。整个调用的路径又变成了ngx_conf_parse()->ngx_conf_handler()。而ngx_conf_handler搜索到"server"指令的处理函数其实是由ngx_http_core_module的ngx_http_core_server()方法来解析。我们只关注这个函数最核心的部分:
static char *
ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
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;
}
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
continue;
}
module = ngx_modules[i]->ctx;
if (module->create_srv_conf) {
mconf = module->create_srv_conf(cf);
if (mconf == NULL) {
return NGX_CONF_ERROR;
}
ctx->srv_conf[ngx_modules[i]->ctx_index] = mconf;
}
if (module->create_loc_conf) {
mconf = module->create_loc_conf(cf);
if (mconf == NULL) {
return NGX_CONF_ERROR;
}
ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
}
}
/* the server configuration context */
cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];
cscf->ctx = ctx;
// cmcf是http_ctx->main_conf[ngx_http_core_module]
cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
// 这个函数获取到数组第一个可用的项
cscfp = ngx_array_push(&cmcf->servers);
if (cscfp == NULL) {
return NGX_CONF_ERROR;
}
*cscfp = cscf;
/* parse inside server{} */
pcf = *cf;
cf->ctx = ctx;
cf->cmd_type = NGX_HTTP_SRV_CONF;
rv = ngx_conf_parse(cf, NULL);
*cf = pcf;
return rv;
}这个函数
- 首先为自己分配了一个ngx_http_conf_ctx_t结构;
- 初始化该结构,将该结构的main_conf指向http_ctx的main_conf,同时创建srv_conf和loc_conf指针数组,
- 依此调用每个http模块的create_srv_conf和create_loc_conf的回调方法(可不必实现),
接下来,就解析位于server{}块内的配置了,解析的方法也是调用ngx_conf_parse,只是此时的上下文环境又发生了改变:cf->ctx = ctx;cf->cmd_type = NGX_HTTP_SRV_CONF。同时在此之前,会将该server对应的ngx_http_conf_ctx_t结构放在cmcf->servers数组中:
// cmcf是http_ctx->main_conf[ngx_http_core_module]
cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
// 这个函数获取到数组第一个空闲项
cscfp = ngx_array_push(&cmcf->servers);
if (cscfp == NULL) {
return NGX_CONF_ERROR;
}
*cscfp = cscf;
用文字来表述也就是:在http{}对应的ngx_http_core_module的main配置ngx_http_core_main_conf_t中有一个数组servers存储了http{}块下的所有的server的结构,主要是便于后续merge。以后在解析的时候没遇到一个server {},就会将该server对应的数据结构添加到servers数组中。
至此,nginx解析到这里的时候在内存中形成的配置项结构如下图所示:
对上图的理解:
- 对于http模块的每个层级配置,均有一个ngx_http_conf_ctx_t结构与之对应,如"http"指令对应的粉色的方框以及两个server指令对应的蓝色方框;
- http块下所有的server块对应的ngx_http_conf_ctx_t结构均在存储在http块对应的ngx_http_conf_ctx_t的main_conf的ngx_http_core模块的ngx_http_core_main_conf_t的servers字段中,该字段的类型是array;
- 每个server的ngx_http_conf_ctx_t内又会创建srv_conf和loc_conf数组,以解析server{}内的配置,但不需要创建main_conf数组了,main_conf直接指向父配置块的main_conf,如图上的两条虚线
本文深入探讨Nginx配置文件的解析原理,包括http模块配置解析及内存映射,详细解析了ngx_http_block和ngx_http_core_server函数的工作流程。
1121

被折叠的 条评论
为什么被折叠?



