我们先来看看http module的类型的结构体:
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);
char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
void *(*create_srv_conf)(ngx_conf_t *cf);
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
void *(*create_loc_conf)(ngx_conf_t *cf);
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
void *(*create_main_conf)(ngx_conf_t *cf);
char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
void *(*create_srv_conf)(ngx_conf_t *cf);
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
void *(*create_loc_conf)(ngx_conf_t *cf);
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;
中间有两项被加粗的成员,这就是要讨论的重点了,其他的项都好说,这里有点不太明确的就是preconfiguration
和postconfiguration,这两个handler的一般作用分别是:
preconfiguration:设置一些全局预定义的变量,在之后的解析中可能要用到
postconfiguration: 字如其名,一般是在配置解析完之后,设置一些处理handler或者filter handler
关于merge,中文翻译一般为:合并,那么谁跟谁合并,为什么要合并,就是我们要讨论的问题!
这里先交代一个背景,关于nginx的配置信息组织结构。
http
|__server
| |__location
| |__location
|
|__server
|__location
nginx每当解析一个server或者location是都以一个ctx(类型是ngx_http_conf_ctx_t),即所谓的上下文变量来保存
当前(即局部)的配置,同时上层的ctx可以从cf->ctx的形式得到,在这个当前ctx中它的main_conf会继承上层,这点上
server和location是一样的处理,而若当前是server,那么它ctx中的src_conf和loc_conf需要重新创建,而location处理时,
则只会创建loc_conf,src_conf共享上层server的。
函数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)
{
char *rv;
ngx_uint_t s;
ngx_http_conf_ctx_t *ctx, saved;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t **cscfp;
// http block中配置的server块,都放在cmcf->servers这个数组中
cscfp = cmcf->servers.elts;
// 在外层的处理中cf->ctx的main_conf,srv_conf和loc_conf都已初始化过了
// 但是大部分都是空壳,即简单的create,没有有针对配置的具体指令来设置
// 真正有用的设置,要到递归到内层(server block和location block)时才会做
ctx = (ngx_http_conf_ctx_t *) cf->ctx;
// 先保存上层ctx,保持其“纯洁性”。nginx这一套配置的层次设计所要求的,每次的结构都一样,
// 但是具体的信息不同,也就是说,在server和location的解析中每个层次都有自己的ngx_http_conf_ctx_t结构
// 外层只是一个架子,内层会针对具体配置来填充自己的ngx_http_conf_ctx_t结构中src_conf,loc_conf的项目。
saved = *ctx;
rv = NGX_CONF_OK;
for (s = 0; s < cmcf->servers.nelts; s++) {
ctx->srv_conf = cscfp[s]->ctx->srv_conf;
if (module->merge_srv_conf) {
/*
* saved.srv_conf是上层srv_conf的数组,cscfp[s]>ctx->srv_conf是下层srv_conf的组数,他们都包含了
* 各个module中srv_conf,ctx_index就是某个具体的module在srv_conf数组中的下标,通过这个下标
* 就可以拿到该module的srv_conf.那么一个上层的一个下层的,要做什么呢?这里以ngx_http_core_merge_srv_conf
* 函数为例,看看它的原理是什么,loc_conf的处理是一样的,这里就不介绍了。
*/
rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index],
cscfp[s]->ctx->srv_conf[ctx_index]);
.......
}
.....
return rv;
}
下面摘录下ngx_http_core_merge_srv_conf的部分代码:
ngx_http_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_core_srv_conf_t *prev = parent; // 上层
ngx_http_core_srv_conf_t *conf = child; // 下层
ngx_conf_merge_size_value(conf->connection_pool_size,
prev->connection_pool_size, 256);
....
}
关于这个宏的解释:
/*
* 意思很明确,如果下层的某个变量没有配置,那么就用上层(若有设置的话)的,因为我们在使用srv_conf或者
* loc_conf的时候一般都是要用到底层的,也就是具体请求对应的location的配置,很多srv_conf的配置是需要传递下来
* 供下层使用的,(main_conf底层跟上层都是共享的)
*/
#define ngx_conf_merge_size_value(conf, prev, default) \
if (conf == NGX_CONF_UNSET_SIZE) { \
conf = (prev == NGX_CONF_UNSET_SIZE) ? default : prev; \
}
以上的分析是否是将前面提到的论点讲述清楚了呢?这就是nginx的merge动作的实质。