概览
Nginx 的配置解析机制设计得非常灵活,以便支持高度模块化和分层配置。本文将带你从源码角度梳理 Nginx 配置解析的全过程,包括核心模块配置、HTTP 模块分层配置以及最终的运行时优化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 断点配置
// 可看到每个关键字解析方式
3 breakpoint keep y 0x000055555557a545 in ngx_conf_handler at src/core/ngx_conf_file.c:328
static ngx_int_t
ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
{
...
for (i = 0; ngx_modules[i]; i++) {
...
cmd = ngx_modules[i]->commands;
}
1. 核心模块初始化(ngx_init_cycle)
Nginx 启动时,首先需要为核心模块(NGX_CORE_MODULE)分配并初始化配置结构。核心模块包括日志模块、错误处理模块等,它们是 Nginx 能够运行的基础。
核心代码片段如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_CORE_MODULE) {
continue;
}
module = ngx_modules[i]->ctx;
// 调用模块自己的 create_conf 方法创建配置结构
if (module->create_conf) {
rv = module->create_conf(cycle);
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
// 将创建好的配置结构挂载到全局 conf_ctx 数组
cycle->conf_ctx[ngx_modules[i]->index] = rv;
}
}
解释:
ngx_modules[i]->type:判断模块类型,仅处理核心模块。module->create_conf(cycle):调用模块自己提供的创建配置结构函数。cycle->conf_ctx[ngx_modules[i]->index]:将模块配置结构存入全局数组,方便后续访问。
核心模块的配置结构通常比较简单,不涉及多层嵌套。
2. 配置解析主入口(ngx_conf_parse)
核心模块配置初始化完成后,Nginx 会进入 配置解析阶段,读取 nginx.conf 文件并逐条处理指令:
1
2
3
4
5
if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
ngx_conf_parse 是配置解析的入口,它会:
- 读取配置文件的每一行;
- 根据指令匹配对应的模块命令;
- 调用回调函数进行配置结构赋值。
3. 配置解析核心(ngx_conf_parse → ngx_conf_handler)
配置解析的关键在于两步:
1
2
3
4
5
// 取出模块自己的配置结构
conf = ((void **) cf->ctx)[ngx_modules[i]->index];
// 执行指令回调,将值写入模块配置结构
rv = cmd->set(cf, cmd, conf);
- 取结构体:从全局
conf_ctx数组中找到模块自己的配置结构。 - 赋值:调用模块命令对应的
set回调函数,将指令的值写入配置结构中。
对于核心模块,这个配置结构就是一层,直接存储在
conf_ctx数组中。
4. HTTP 模块的特殊处理(ngx_http_block)
当解析器遇到 http {} 指令时,会调用 HTTP 模块专用的 block 处理函数:
1
ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
HTTP 模块配置结构
HTTP 模块的配置结构更加复杂,分为多层:
| 层级 | 说明 |
|---|---|
| Main | main_conf,全局 HTTP 配置 |
| Server | srv_conf,每个 server 块配置 |
| Location | loc_conf,每个 location 块配置 |
每个 HTTP 模块都会在 ngx_http_module_t 或自定义 ctx 中定义如下回调函数:
1
2
3
4
5
6
7
8
9
10
11
NULL, /* preconfiguration方法 */
ngx_http_myfilter_init, /* postconfiguration方法 */
NULL, /* create_main_conf方法 */
NULL, /* init_main_conf方法 */
NULL, /* create_srv_conf方法 */
NULL, /* merge_srv_conf方法 */
ngx_http_myfilter_create_conf, /* create_loc_conf方法 */
ngx_http_myfilter_merge_conf /* merge_loc_conf方法 */
create_*_conf:分配并初始化配置结构merge_*_conf:将父层配置合并到子层preconfiguration/postconfiguration:模块初始化时执行的钩子init:用于进一步初始化
5. HTTP 配置的收尾处理
HTTP 模块在配置解析完成后,还需要进行 运行时数据结构初始化和优化:
1
2
3
4
5
6
7
if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
return NGX_CONF_ERROR;
}
5.1 初始化阶段处理器(Phase Handlers)
每个 HTTP 请求在 Nginx 内部分为多个阶段,例如:
- post-read
- server-rewrite
- find-config
- rewrite
- pre-access
- access
- try-files / content
- log
- 模块可以注册对应阶段的处理函数。
ngx_http_init_phase_handlers()遍历所有 server/location,将这些函数挂到对应阶段数组,便于请求处理时快速调用。
5.2 优化服务器/端口/域名查找
ngx_http_optimize_servers()对所有 server 配置按端口、IP、域名排序,建立查找表。- 运行时 HTTP 请求到来时可以高效匹配到对应 server/location。
- 这一步是配置解析后的“性能优化”,不改变原始配置内容。
6. 总结 Nginx 配置解析流程
核心模块初始化
ngx_init_cycle遍历所有核心模块,调用create_conf为每个核心模块分配配置结构。
解析配置文件
ngx_conf_parse按行解析指令,找到对应模块命令并调用cmd->set写入模块配置结构。
HTTP / Event / Stream 模块处理
- 遇到
http {}等块时,调用对应 block 处理函数,如ngx_http_block。 - 为每个模块分配多层配置结构(main/srv/loc),并调用模块回调函数。
- 遇到
运行时初始化和优化
ngx_http_init_phase_handlers():初始化请求阶段处理函数。ngx_http_optimize_servers():优化 server/port/域名查找表。
通过以上流程,Nginx 实现了模块化、分层、灵活而高效的配置系统。