上一篇文章分析了nginx.conf配置解析流程,解析完成后会把各个配置项存放到各个模块的上下文结构中。但此时还没有对http模块、server模块、location模块公共部分进行合并处理。所谓的合并: server块的模块上下文没有值,则继承http模块的模块上下文值。location块的模块上下文没有值,则继承server块模块上下文的值。接下来分析nginx是如何合并http模块、server模块、location模块公共部分。
假设nginx.conf配置文件结构如下
http
{
//server块1
server
{
//location块1
location
{
}
//location块2
location
{
}
}
//server块2
server
{
}
}
这是nginx.conf配置文件的一个框架结构,其中省略了其他配置项。http模块下有两个server块。其中第一个server块下还有两个location块。解析配置后,http模块的上下文结构如下图:
http块下有两个server块,分别为server块1,server块2。这两个server块是通过数组形式连接起来。http块中的main_conf[0]核心模块上下文结构为ngx_http_core_main_conf_t,它的成员servers就是一个数组,存放这这两个server块。
ngx_http_core_loc_conf_s结构中的成员locations是一个队列,队列元素为某个server块下的各个location结构。图中server块1下有两个location块,server1块loc_conf中的核心模块上下文结构的ocations,是由这两个location块组成的队列。
也就是说server块是通过数组串接起来的, location块是通过链表串接起来的。
一、http块与server块的合并
http块与server块的合并过程分析:包含server块的合并,location块的合并。
(1)server块的合并: 对于每一个模块,在每一个server块中,找到模块在srv_conf中的位置,然后将该模块上下文与http块中srv_conf对应模块所在位置模块上下文进行比较。如果server块中的上下文没有值,则使用http上下文的值,否则使用server块中模块上下文自己的值。以ngx_core_module模块为例,这个模块是第一个模块,存放在srv_conf下标为0的位置。首先将server1块中的srv_conf[0]与http块的srv_conf[0]进行合并, 然后将server2块中的srv_conf[0]与http块的srv_conf[0]进行合并。这样server块的合并就完成了。
(2)location块的合并: 对于每一个模块,在每一个server块中,找到模块在loc_conf中的位置,然后将该模块上下文与http块中loc_conf对应模块所在模块上下文进行比较。如果location块中的上下文没有值,则使用http上下文中的值,否则使用location块中模块上下文自己的值。以ngx_core_module模块为例,这个模块是第一个模块,存放在loc_conf下标为0的位置。首先将server1块中的loc_conf[0]与http块的loc_conf[0]进行合并, 然后将server2块中的lco_conf[0]与http块的loc_conf[0]进行合并。这样loc块的合并就完成了。
char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
for (m = 0; ngx_modules[m]; m++)
{
if (ngx_modules[m]->type != NGX_HTTP_MODULE)
{
continue;
}
//(1)将http中的server与server块的server合并
//(2)将http中的loction与server块的location合并
//(3)将server块中的location与localtion中的location合并
rv = ngx_http_merge_servers(cf, cmcf, module, mi);
}
}
而其中的函数ngx_http_merge_servers负责具体的合并过程。这个函数一共使用了3个合并过程,先分来http块与server的合并。
//cmcf = ctx->main_conf[ngx_http_core_module.ctx_index],其中ctx是http块的ngx_http_conf_ctx_t结构
//module指的是模块上下文
//ctx_index指的是该模块在同一类型中的位置
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)
{
//cscfp指向所有http块下的所有server块
cscfp = cmcf->servers.elts;
//ctx指向http块的ngx_http_conf_ctx_t结构
ctx = (ngx_http_conf_ctx_t *) cf->ctx;
//遍历http块下有所有server块
for (s = 0; s < cmcf->servers.nelts; s++)
{
//指向每一个server块中的srv_conf结构
ctx->srv_conf = cscfp[s]->ctx->srv_conf;
if (module->merge_srv_conf)
{
//将http块中的某个server与server块中的某个server进行合并
//saved.srv_conf[ctx_index]是http块下的srv_conf中某个元素
//cscfp[s]->ctx->srv_conf[ctx_index]是server块下的srv_conf中某个元素
rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index], cscfp[s]->ctx->srv_conf[ctx_index]);
}
if (module->merge_loc_conf)
{
ctx->loc_conf = cscfp[s]->ctx->loc_conf;
//将server块中loc_conf的某个loc与http块中loc_conf的某个loc合并
rv = module->merge_loc_conf(cf, saved.loc_conf[ctx_index], cscfp[s]->ctx->loc_conf[ctx_index]);
}
}
}
二、server块与location块的合并
server块与location块合并的内容就比较少了,只需要将各个location块中的loc_conf与server块中的loc_conf进行合并。
location块的合并: 对于每一个模块,在每一个location块中,找到模块在loc_conf中的位置,然后将该模块上下文与所属server块中loc_conf对应模块所在模块上下文进行比较。如果location块中的上下文没有值,则使用所属server块上下文中的值,否则使用location块中模块上下文自己的值。以ngx_core_module模块为例,这个模块是第一个模块,存放在loc_conf下标为0的位置。首先将location1块中的loc_conf[0]与server块1的loc_conf[0]进行合并, 然后将locatio2块中的lco_conf[0]与server1块的loc_conf[0]进行合并。这样loc块的合并就完成了。
函数ngx_http_merge_locations负责server块与location块的合并。当然如果location还嵌套location块,则会递归合并location块与location内的所有子locaton块。
//locations为server块中ngx_http_core_module的lociton中队列
//loc_conf为server块的loc_conf
//module为模块上下文
//ctx_index指module对应模块在同一类模块中的索引
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)
{
for (q = ngx_queue_head(locations);
q != ngx_queue_sentinel(locations);
q = ngx_queue_next(q))
{
//合并server块与locatin块中相应的location
//loc_conf[ctx_index]为server块中loc_conf的某个loc
//clcf->loc_conf[ctx_index]为location块中的loc_conf的某个loc
rv = module->merge_loc_conf(cf, loc_conf[ctx_index], clcf->loc_conf[ctx_index]);
//递归合并location块下的location
//clcf->locations为location块中的loc_conf某个loc对应的队列
//clcf->loc_conf为location块中的loc_conf
rv = ngx_http_merge_locations(cf, clcf->locations, clcf->loc_conf, module, ctx_index);
}
}
最终ngx_core_module模块的合并结果如下:
以上只是ngx_core_module模块的合并结果,其它模块合并的过程也是一样的。最终一个for循环,就可以合并所有http模块。这样每一个http模块的上下文结构都有值,这个值要么来自nginx.conf配置文件,要么继承上一层结构。