您的位置:首页 > 理论基础 > 计算机网络

Nginx HTTP模块的配置项管理【2】

2014-12-28 18:03 162 查看
本篇文章将具体介绍一下Nginx解析HTTP模块配置文件的流程。

核心模块ngx_http_module是进入HTTP模块解析的入口。该模块仅仅定义了一个ngx_command_t结构体,用于声明遇到http{}时需要进入的处理函数

static ngx_command_t  ngx_http_commands[] = {

{ ngx_string("http"),
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
//开始解析http模块相关的配置项
ngx_http_block,
0,
0,
NULL },

ngx_null_command
};

而ngx_http_core_module则定义了默认的配置文件的解析规则、create_XXX_conf生成的结构体的类型等。先说一下所有级别的配置项是如何在内存中关联起来的,ngx_http_core_module定义了三种配置数据结构ngx_http_core_main_conf_t、ngx_http_core_srv_t、ngx_http_core_loc_t。对于ngx_http_core_main_conf_t来说,它包含一个servers动态数组,用于指向所有由ngx_http_core_module创建的ngx_http_core_srv_conf_t数据结构。而ngx_http_core_srv_conf_t
数据结构中又包含了指向它上下文的指针;而ngx_http_core_loc_conf_t中有一个locations的双向循环链表,它可以将不同级别的属于location的配置项关联起来。借用《Nginx模块开发与架构解析》中的图:





这样的关系是ngx_http_core_module中定义的。但是由于ngx_http_conf_ctx的关联,可以找到所有模块的配置结构体。

http{}级别解析

所以当遇到http{}时,调用ngx_http_block函数,在这个函数中,将会调用ngx_http_module_t公共接口中的回调函数,其中关于配置项解析的处理流程是这样的:

1、创建配置上下文结构体ngx_http_conf_ctx_t,并为其包含的三个数组分配空间。

2、对于每一个HTTP模块,如果定义了create_main_conf函数,则调用它,将产生的结构体按照模块的序号加入到ngx_http_conf_ctx_t的main_conf数组中;如果定义了create_srv_conf函数,则调用它,将产生的结构体按照模块的序号加入到ngx_http_conf_ctx_t的srv_conf数组中;如果定义了create_loc_conf函数,则调用它,将产生的结构体按照模块的序号加入到ngx_http_conf_ctx_t的loc_conf数组中。

3、调用每个HTTP模块的preconfiguration函数。

4、调用ngx_conf_parse函数真正进行解析,该函数扫描配置文件,遇到模块的ngx_command_t中匹配的配置项时,调用其set函数,进行参数值的提取或者继续进行server级别和main级别配置块的解析。执行完这个函数后,各个配置结构体的成员的值已经填充完毕。

5、调用每个模块的init_main_conf函数。

6、调用merge_srv_conf和merge_loc_conf函数进行配置项的合并。合并包括http与srv级别的server有关的配置项的合并、http与server级别的location有关的配置项合并、以及server与location级别的location相关的配置项合并以及location级别嵌套的location相关配置项的合并(比较绕。。。)。我们在上面介绍了,由于它们以一种比较复杂的关系关联起来,可以很方便的将不同级别的配置结构体做合并操作。

ngx_http_core_module定义了很多个ngx_command_t,我大致列举了几个:

static ngx_command_t  ngx_http_core_commands[] = {
...

{ ngx_string("server"),
NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_MULTI|NGX_CONF_NOARGS,
ngx_http_core_server,
0,
0,
NULL },

//本配置项属于server相关的配置项,但它可以是main级别的
//也可以是srv级别的
{ ngx_string("connection_pool_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
//#define NGX_HTTP_SRV_CONF_OFFSET   offsetof(ngx_http_conf_ctx_t, srv_conf)
NGX_HTTP_SRV_CONF_OFFSET,
//connecton_pool_size相对于ngx_http_core_srv_conf_t的偏移
//ngx_http_core_pool_size_p解析完配置项的后续handler函数
offsetof(ngx_http_core_srv_conf_t, connection_pool_size),
&ngx_http_core_pool_size_p },

...

{ ngx_string("location"),
NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,
ngx_http_core_location,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },

{ ngx_string("listen"),
NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
ngx_http_core_listen,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },

{ ngx_string("server_name"),
NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
ngx_http_core_server_name,
NGX_HTTP_SRV_CONF_OFFSET,
0,
NULL },

...

#endif

ngx_null_command
};


server级别解析

可见,在解析http级别的配置项时,遇到server{}时,会调用ngx_http_core_module模块进行server级别的配置项的解析。与http级别配置项解析相同,它也会生成一个ngx_http_conf_ctx_t结构体。

具体流程如下:

1、创建ngx_http_conf_ctx_t结构体,main_conf将指向http块下的ngx_http_conf_ctx_t中的main_conf指针,而另两个数组重新分配空间。

2、通过调用create_srv_conf和create_loc_conf函数,按照模块的ctx_index加入到ngx_http_conf_ctx_t结构体的后两个数组中。

3、HTTP模块只有一个http级别的ngx_http_conf_ctx_t上下文,这是全局的。将由ngx_http_core_module模块生成的ngx_http_core_srv_conf_t结构体添加到全局

ngx_http_core_main_conf_t的servers数组中。便于以后将http级别与server级别的有关server配置项的合并。

4、继续调用ngx_conf_parse函数解析。

static char *
ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
char                        *rv;
void                        *mconf;
ngx_uint_t                   i;
ngx_conf_t                   pcf;
ngx_http_module_t           *module;
struct sockaddr_in          *sin;
ngx_http_conf_ctx_t         *ctx, *http_ctx;
ngx_http_listen_opt_t        lsopt;
ngx_http_core_srv_conf_t    *cscf, **cscfp;
ngx_http_core_main_conf_t   *cmcf;

ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}

http_ctx = cf->ctx;
ctx->main_conf = http_ctx->main_conf;

/* the server{}'s srv_conf */

ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->srv_conf == NULL) {
return NGX_CONF_ERROR;
}

/* the server{}'s loc_conf */

ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
if (ctx->loc_conf == NULL) {
return NGX_CONF_ERROR;
}

for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_HTTP_MODULE) {
continue;
}

module = ngx_modules[i]->ctx;

//只调用两个函数了
if (module->create_srv_conf) {
mconf = module->create_srv_conf(cf);
if (mconf == NULL) {
return NGX_CONF_ERROR;
}

ctx->srv_conf[ngx_modules[i]->ctx_index] = mconf;
}

if (module->create_loc_conf) {
mconf = module->create_loc_conf(cf);
if (mconf == NULL) {
return NGX_CONF_ERROR;
}

ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
}
}

/* the server configuration context */

cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];
cscf->ctx = ctx;

//ctx->main_conf指向http级别的ngx_http_conf_ctx_t的main_conf
cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];

//返回的是要插入的内存地址
cscfp = ngx_array_push(&cmcf->servers);
if (cscfp == NULL) {
return NGX_CONF_ERROR;
}
//插入cscf
*cscfp = cscf;

/* parse inside server{} */

pcf = *cf;
//重要 更新上下文!以便调用下面的ngx_conf_parse的时候上下文信息
//与当前级别相符。
cf->ctx = ctx;
cf->cmd_type = NGX_HTTP_SRV_CONF;

rv = ngx_conf_parse(cf, NULL);

*cf = pcf;

if (rv == NGX_CONF_OK && !cscf->listen) {
ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));

sin = &lsopt.u.sockaddr_in;

sin->sin_family = AF_INET;
#if (NGX_WIN32)
sin->sin_port = htons(80);
#else
sin->sin_port = htons((getuid() == 0) ? 80 : 8000);
#endif
sin->sin_addr.s_addr = INADDR_ANY;

lsopt.socklen = sizeof(struct sockaddr_in);

lsopt.backlog = NGX_LISTEN_BACKLOG;
lsopt.rcvbuf = -1;
lsopt.sndbuf = -1;
#if (NGX_HAVE_SETFIB)
lsopt.setfib = -1;
#endif
lsopt.wildcard = 1;

(void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.addr,
NGX_SOCKADDR_STRLEN, 1);

if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) {
return NGX_CONF_ERROR;
}
}

return rv;
}


location级别解析

在解析srv级别配置项时,如果发现location{}配置块则调用ngx_http_core_location方法。

(1) 在解析location{}配置块时,仍然会像解析Http块一样,先建立ngx_http_conf_ctx_t结构体,只是这里的main_conf和srv_conf都将指向所属的server块下的ngx_http_conf_ctx_t结构体的main_conf和srv_conf指针数组,而loc_conf则将指向重新分配的指针数组。

(2) 循环调用所有HTTP模块的create_loc_conf方法,将返回的结构体指针按照模块序号ctx_index保存到上述的loc_conf指针数组中。

(3) 如果在location中使用了正则表达式,那么这时将调用pcre_compile方法预编译正则表达式,提高性能。

(4) 第一个HTTP模块是ngx_http_core_module模块,它在create_loc_conf方法中将会生成ngx_http_core_loc_conf_t配置结构体,可以认为该结构体对应着当前解析的location块。这时会生成ngx_http_location_queue_t结构体,因为每一个ngx_http_core_loc_conf_t结构体都对应着一个ngx_http_location_queue_t,因此,此处将把ngx_http_location_queue_t串联成双向链表。

(5) 正式解析当前location{}配置块内的loc级别配置项。

配置项合并

合并就不说了,就是用到了结构体中特有servers、ctx以及locations这些成员将所有配置项关联起来。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: