以前我们详细了分析了 nginx 配置解析的各个过程,尤其是各种 conf 结构的创建、初始化、储存。那么现在我们要分析下 merge 的过程,一来它确实是配置解析过程的一个部分,二来我们可以检验下我们对各种 conf 结构存储的位置是否真正的清晰明了。
首先我们了解下 merge 的背景:
- 所谓 merge 操作,就是合并内外层的配置。大体原则是:如果内层没有配置,那么以外层为准,如果都没有配置,那么就用默认值;
- NGX_CORE_MODULE 模块的 ctx(ngx_core_module_t)是没有 merge 操作的,所以像 http 块这一层的配置是不需要和上一层去 merge 的,想想也明白为什么,http 哪来的上一层呢?
- NGX_HTTP_MODULE 模块的 ctx(ngx_http_module_t)是有 merge 操作的,但是仅仅有 merge_srv_conf 和 merge_loc_conf,同理对 main 层不需要 merge;
- merge 操作发生的时机是在 ngx_http_block函数中(即 http 块解析函数),在递归调用 ngx_conf_parse 之后。这是为了让 http 块之内所有的指令都解析结束,然后再去做 merge 操作;
- 不同层级块的逻辑关系,基本上都是放在 ngx_http_core_module 这个模块的不同级别的 conf 中,在 merge 中会频繁用到。
来看看 ngx_http_block 中的一段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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;
/* init http{} main_conf's */
if
(module->init_main_conf) {
rv = module->init_main_conf(cf, ctx->main_conf[mi]);
if
(rv != NGX_CONF_OK) {
goto
failed;
}
}
rv = ngx_http_merge_servers(cf, cmcf, module, mi);
if
(rv != NGX_CONF_OK) {
goto
failed;
}
}
|
再明显不过了,init 了 main,merge 了 server。 这里的处理流程是个 for 循环,每次的迭代对象是模块本身,也就是挨个模块的去处理 merge。
在进入 ngx_http_merge_servers 内部分析之前,我们先确认一下这些代入的参数:
- cf 是代入的参数,但是我们真正关心的还是 cf->ctx,这个时候它其实就是 http 块的三元组(代入 ngx_http_block 的时候还不是,但是在函数中赋值了);
- cmcf 这个 http 块的 ngx_http_core_module 的 main_conf 结构;
- module 是个循环获取的,代表当前模块;
- mi 就是当前模块在 NGX_HTTP_MODULE 模块中的 index;
这里很奇怪的就是并没有明确指出 merge loc_conf 的地方,我们推测它是在 ngx_http_merge_servers 中实现的,那么我们具体的分析下它的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|