Nginx源码学习——配置项生效的大体流程
摘要:Nginx服务在启动时就会读取配置文件,配置文件影响着服务的工作方式、性能优化等诸多方面,我们以core和event模块为例,看看在Nginx服务的启动过程中,配置项是怎么一步步从被读取到最终生效的。
配置文件路径
一般来说,配置文件名为:nginx.conf
. 保存在/usr/local/nginx/conf, /etc/nginx, 或 /usr/local/etc/nginx
.
也可以由用户指定路径,通过命令行传递给进程,如:nginx -c ./nginx.conf
Nginx服务启动后,main() 通过解析命令行参数、获取当前可执行文件目录等操作,最终取得了完整的nginx.conf配置文件路径。
解析配置文件并存储配置参数
main()执行初始化过程中,调用ngx_init_cycle函数,该函数是nginx启动过程中非常重要的一环。
它负责许多重要的初始化工作,其中与配置相关的可分为三步:
- 调用所有核心模块的create_conf方法,生成存放配置项的结构体。
- 针对所有核心模块解析nginx.conf配置文件。
- 解析配置文件完成后,调用所有核心模块的init_conf方法,初始化各核心模块配置项结构体。
我们重点来说第1步和第3步,第2步解析过程也有许多细节,但这里我们不在详述。
回调各个核心模块的create_conf函数:
//遍历模块数组,找到核心模块,获得核心模块的上下文结构体指针ctx,使用该指针调用回调函数create_conf
//以获取一块内存保存配置项数据结构,返回指向该内存的指针,根据模块索引保存指针到conf_ctx数组中
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->type != NGX_CORE_MODULE) {
continue;
}
module = cycle->modules[i]->ctx;
if (module->create_conf) {
rv = module->create_conf(cycle);
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
cycle->conf_ctx[cycle->modules[i]->index] = rv;
}
}
解析配置文件完成后,回调各核心模块的的init_conf函数,初始化各模块配置项结构体:
//解析配置项完成后,遍历模块数组,找到核心模块,获得核心模块的上下文结构体指针ctx,
//使用该指针调用回调函数init_conf,同时,根据模块索引从conf_ctx数组中获取指向存储配置项
//的结构体变量的指针,将该指针作为参数之一传入init_conf函数
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->type != NGX_CORE_MODULE) {
continue;
}
module = cycle->modules[i]->ctx;
if (module->init_conf) {
if (module->init_conf(cycle,
cycle->conf_ctx[cycle->modules[i]->index])
== NGX_CONF_ERROR)
{
environ = senv;
ngx_destroy_cycle_pools(&conf);
return NULL;
}
}
}
在Nginx源码学习——从数据结构看模块划分 一文中可知create_conf和init_conf回调函数是如何能够工作的,以及模块间的层次关系。
对于core模块来说,有这样一个模块上下文(module context)
结构体:
static ngx_core_module_t ngx_core_module_ctx = {
ngx_string("core"),
ngx_core_module_create_conf,//创建存储配置项的数据结构
ngx_core_module_init_conf; // 初始化配置项数据结构
};
ngx_core_module_create_conf
就是ngx_init_cycle
函数内回调的core模块的create_conf
函数。
ngx_core_module_init_conf
就是ngx_init_cycle
函数内回调的core模块的init_conf
函数。
同理,对于event
模块,其通用接口ngx_event_module_t 定义的上下文结构体(module context)
变量:
static ngx_event_module_t ngx_event_core_module_ctx = {
&event_core_name,
ngx_event_core_create_conf, /* create configuration */
ngx_event_core_init_conf, /* init configuration */
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};
ngx_event_core_create_conf
函数就是ngx_init_cycle
函数内回调的event
模块的create_conf
函数。
ngx_event_core_init_conf
函数就是ngx_init_cycle
函数内回调的event
模块的init_conf
函数。
配置项生效
启动worker进程后,将调用函数ngx_worker_process_init
,内部回调所有模块的init_process
函数,代码片段:
for (i = 0; cycle->modules[i]; i++) {
if (cycle->modules[i]->init_process) {
if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) {
/* fatal */
exit(2);
}
}
}
拿event模块举例,init_process函数是定义ngx_module_t结构体ngx_event_core_module时注册的:
ngx_module_t ngx_event_core_module = {
NGX_MODULE_V1,
&ngx_event_core_module_ctx, /* module context */
ngx_event_core_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master */
ngx_event_module_init, /* init module */
ngx_event_process_init, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
即回调init_process时,执行的是ngx_event_process_init函数。
下面具体看看配置项是如何起作用的,比如worker_connections配置项,将根据该配置项的值决定创建多少连接、读事件和写事件。代码如下所示:
cycle->connections =
ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
if (cycle->connections == NULL) {
return NGX_ERROR;
}
c = cycle->connections;
cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
cycle->log);
if (cycle->read_events == NULL) {
return NGX_ERROR;
}
rev = cycle->read_events;
for (i = 0; i < cycle->connection_n; i++) {
rev[i].closed = 1;
rev[i].instance = 1;
}
cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,
cycle->log);
if (cycle->write_events == NULL) {
return NGX_ERROR;
}
wev = cycle->write_events;
for (i = 0; i < cycle->connection_n; i++) {
wev[i].closed = 1;
}
cycle->connection_n 即是worker_connections配置项的值。可见,connection_n的值决定了连接、读写事件的数量。