在前一篇文章(二)中我们说到了"server"指令的解析过程,并且知道了每遇到一个"server"时便会创建一个ngx_http_conf_ctx_t结构,初始化该结构并且将其放在一个特定数组中。然后开始对server{...}内部指令开始解析,本文主要就来描述server块内部的解析流程。
对于server{...}内部的指令解析的原理其实也比较简单:同样是对每个指令执行"加载-匹配-设置"的循环往复。但是由于server{...}内部可能嵌套location... {...}块,因此一切也就变得不太一样了,因此,本文重点描述对于"loc"指令的解析。
"location"指令是由ngx_http_core_module负责解析,而解析函数是ngx_http_core_location
ngx_http_core_location
首先我们来简单看看这个函数被调用时的上下文(由于"location"是出现在server{...}配置块中,因此,调用者的上下文设置是在函数ngx_http_core_server中设置):
static char *
ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
......
cf->ctx = ctx;
cf->cmd_type = NGX_HTTP_SRV_CONF;
......
}
接下来再看看ngx_http_core_location的解析过程:
static char *
ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
pctx = cf->ctx;
ctx->main_conf = pctx->main_conf;
ctx->srv_conf = pctx->srv_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_loc_conf) {
ctx->loc_conf[ngx_modules[i]->ctx_index] =
module->create_loc_conf(cf);
if (ctx->loc_conf[ngx_modules[i]->ctx_index] == NULL) {
return NGX_CONF_ERROR;
}
}
}
......
pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];
......
if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
save = *cf;
cf->ctx = ctx;
cf->cmd_type = NGX_HTTP_LOC_CONF;
rv = ngx_conf_parse(cf, NULL);
*cf = save;
return rv;
对于location指令的解析的主要完成了如下工作:
- 分配ctx = ngx_http_conf_ctx_t结构来代表location {...},后面我们会看到这个配置块非常重要;
- 将ctx->main_conf和ctx->srv_conf分别设置为父配置块的main_conf以及srv_conf,同时为ctx->loc_conf分配内存空间用以存储location {...}配置块内的指令配置;
- 调用每个http模块的create_loc_conf回调,初始化2中的loc_conf数组(void *)数组
- 接下来是重点:pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index]:通过这个来获取父配置块(代表location指令所属的server{...}的ngx_http_conf_ctx_t)的http_core_module的loc_conf配置pclcf,我们下面会使用该结构的locations成员;
- 调用ngx_http_add_location(cf, &pclcf->locations, clcf)来将该location块的ngx_http_conf_ctx_t结构添加至pclcf->locations队列中,这样,每个server就可以通过该结构来管理下面的所有location配置;
- 完成了上面 的主要解析工作以后,我们还需要进入location ... {...}块内部进行解析,在解析前我们需要知道此时上下文环境已经发生了变化,因此,需要重新设置:cf->ctx = ctx; cf->cmd_type = NGX_HTTP_LOC_CONF;
我想,下图能够比较清晰地表达出server配置块的数据结构和location配置块数据结构之间的关系:
有一个疑问:我真的理解了ngx_http_conf_ctx_t中各个成员变量的含义了吗?显然没有,比如:代表server {...}配置块的ngx_http_conf_ctx_t的结构的loc_conf的真正含义到底是什么?