本文共 4576 字,大约阅读时间需要 15 分钟。
考虑有如下形式的配置文件:
http{ .... server { ..... location { ..... } }}
根据前面的讲解,Nginx会为每个http块,server块和location块创建一个ngx_http_conf_ctx_t结构体。这个结构体主要是为了管理各个模块的main_conf,srv_conf,loc_conf。
我们知道,对于http块,Nginx会为每个模块创建他的main_conf,srv_conf,loc_conf;对与每个server块,Nginx会为每个模块创建srv_conf,loc_conf;对于每个location块,Nginx会为每个模块创建它的loc_conf。
按照朴素的想法,这种构建构建结构体的方式会导致很多冗余。比如在http块中,按道理应该只创建main_conf,而不必创建srv_conf和loc—_conf,而在server块中,只需要创建srv_conf,在location块中应该只创建loc_conf。这样才比较合理。但NGINX选择了这种冗余的创建方式,主要是基于灵活性的考虑:
比如对于如下这样额配置:
http{ server_name="wen"; server { server_name="guang"; }}
对于srv_conf类型的配置项server_name的值,可以有两个选择。而merge过程就是决定选哪个作为最终的值。就是合并。
http{ pp=12; server { server_name="wen"; } server { server_name="shan"; }}
对于srv_conf类型的配置项pp,为了让所有server块中的模块的srv_conf配置结构体的pp项都包含相同的值,不需要在每个server块里面都写pp=12,只需要在最外层写就可以了,然后merge过程会把这个pp配置项的值merge到所有模块的drv_conf中。
下面我们具体讲一下merge的过程。
merge过程的入口是ngx_http_merge_servers
函数。
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)
根据这个函数的参数,我们可以理解为是对一个模块-module的配置结构体的合并。主要包括以下三个方面的合并:
这里需要注意一下的是:我们说的server块下的location块是指server块下的直属location块。location块下的location块只的也是location的直属下的location块。
我们详细看一下代码,代码也是按照上面的三个阶段进行讲解:
for (s = 0; s < cmcf->servers.nelts; s++) { /* merge the server{}s' srv_conf's */ ctx->srv_conf = cscfp[s]->ctx->srv_conf; if (module->merge_srv_conf) { rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index], cscfp[s]->ctx->srv_conf[ctx_index]); if (rv != NGX_CONF_OK) { goto failed; } }
循环所有的server块。将所有server块下当前module对应的srv_conf和http块中的srv_conf进行合并:
module->merge_srv_conf(cf, saved.srv_conf[ctx_index], cscfp[s]->ctx->srv_conf[ctx_index]);
这个合并的动作是由模块自己实现的,因此行为是模块的提供者自己定义的。没有什么普遍性。
第二阶段就是将http块下的loc_conf和server块下的loc_conf合并:
if (module->merge_loc_conf) { /* merge the server{}'s loc_conf */ ctx->loc_conf = cscfp[s]->ctx->loc_conf; rv = module->merge_loc_conf(cf, saved.loc_conf[ctx_index], cscfp[s]->ctx->loc_conf[ctx_index]); if (rv != NGX_CONF_OK) { goto failed; }
这段代码执行之后,该server块对应的loc_conf就和外层的http块的loc_conf完成了合并。
第三个阶段就是将当前server块中的所有直属下属块和server块的loc_conf合并。由于第二阶段中server块的loc_conf和http块下的loc_conf已经合并了,所以http块的loc_conf也会作用于server下属的location块的loc_conf中。第三个阶段代码如下:
/* merge the locations{}' loc_conf's */ clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index]; rv = ngx_http_merge_locations(cf, clcf->locations, cscfp[s]->ctx->loc_conf, module, ctx_index);
显然,主要任务在ngx_http_merge_locations
函数中完成。
...... for (q = ngx_queue_head(locations); q != ngx_queue_sentinel(locations); q = ngx_queue_next(q)) { lq = (ngx_http_location_queue_t *) q; clcf = lq->exact ? lq->exact : lq->inclusive; ctx->loc_conf = clcf->loc_conf; rv = module->merge_loc_conf(cf, loc_conf[ctx_index], clcf->loc_conf[ctx_index]); if (rv != NGX_CONF_OK) { return rv; } rv = ngx_http_merge_locations(cf, clcf->locations, clcf->loc_conf, module, ctx_index); if (rv != NGX_CONF_OK) { return rv; } }
这个函数的主体和ngx_http_merge_servers
函数还是很相似的。
它会循环当前server下的所有直属location块,然后将这个server块下module的loc_conf分别和各个location块下该module的loc_conf合并。记住,各个层级上平行的location块之间是不发送合并的。
由于location块是允许嵌套location块的。因此我们还需要把这个location块和它直属下属的location块下该module对应的loc_conf合并,因此代码中会递归调用ngx_http_merge_locations
。
到这里就介绍完了Nginx的合并过程。还是很简单的。概括一下大概就是,合并过程是自顶向下的,也就是说,根据层级嵌套,外层的配置项可能会和内层的配置项合并,但是同一个层级的配置项是不会发生合并的。
配置项的合并为配置文件的书写提供了更多的灵活性。一方面多个内层块可以共享共同外层的配置值,另一方面合并操作的自定义特性也提供了更多的可能性。还有就是配置项在配置文件中的书写位置限制更少。
转载地址:http://ypqxi.baihongyu.com/