nginxhttp块配置解析

编程

1. http配置块的解析

        http配置块的解析方法定义在http配置块对应的ngx_command_t结构体中,如下是该结构体的定义:

static ngx_command_t ngx_http_commands[] = {

{ngx_string("http"),

NGX_MAIN_CONF | NGX_CONF_BLOCK | NGX_CONF_NOARGS,

ngx_http_block,

0,

0,

NULL},

ngx_null_command

};

        通过该定义可以看出,http配置块的解析工作主要是交由ngx_http_block()方法进行。如下是该方法的源码:

static char * ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {

char *rv;

ngx_uint_t mi, m, s;

ngx_conf_t pcf;

ngx_http_module_t *module;

ngx_http_conf_ctx_t *ctx;

ngx_http_core_loc_conf_t *clcf;

ngx_http_core_srv_conf_t **cscfp;

ngx_http_core_main_conf_t *cmcf;

if (*(ngx_http_conf_ctx_t **) conf) {

return "is duplicate";

}

// 创建用于存储http模块配置数据的结构体数组

ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));

if (ctx == NULL) {

return NGX_CONF_ERROR;

}

*(ngx_http_conf_ctx_t **) conf = ctx;

// 计算NGX_HTTP_MODULE类型的模块的个数,并且为每个模块的ctx_index赋值

ngx_http_max_module = ngx_count_modules(cf->cycle, NGX_HTTP_MODULE);

// 申请存储配置main conf对象的内存空间

ctx->main_conf = ngx_pcalloc(cf->pool,sizeof(void *) * ngx_http_max_module);

if (ctx->main_conf == NULL) {

return NGX_CONF_ERROR;

}

// 申请存储srv conf对象的内存空间

ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

if (ctx->srv_conf == NULL) {

return NGX_CONF_ERROR;

}

// 申请存储loc conf对象的内存空间

ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

if (ctx->loc_conf == NULL) {

return NGX_CONF_ERROR;

}

// 这里主要是分别调用各个http模块的create_main_conf()、create_srv_conf()和create_loc_conf()

// 方法,以生成对应模块的配置对象,然后将生成的对象放到ctx->main_conf、ctx->srv_conf和ctx->loc_conf

// 数组中。需要注意的是,在将生成的对象放到对应的数组中的时候,所存放的相对位置是通过各个模块的ctx->index

// 属性指定的,也就是其在当前类型的模块中的相对位置

for (m = 0; cf->cycle->modules[m]; m++) {

if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {

continue;

}

// 对于http模块而言,这里的ctx字段指向的是一个ngx_http_module_t结构体对象

module = cf->cycle->modules[m]->ctx;

mi = cf->cycle->modules[m]->ctx_index;

if (module->create_main_conf) {

// 这里的main_conf对应的是解析http块中的NGX_HTTP_MAIN_CONF_OFFSET类型的指令

ctx->main_conf[mi] = module->create_main_conf(cf);

if (ctx->main_conf[mi] == NULL) {

return NGX_CONF_ERROR;

}

}

if (module->create_srv_conf) {

// 这里的srv_conf对应的是解析http块中的NGX_HTTP_SRV_CONF_OFFSET类型的指令

ctx->srv_conf[mi] = module->create_srv_conf(cf);

if (ctx->srv_conf[mi] == NULL) {

return NGX_CONF_ERROR;

}

}

if (module->create_loc_conf) {

// 这里的loc_conf对应的是解析http块中的NGX_HTTP_SRV_CONF_OFFSET类型的指令

ctx->loc_conf[mi] = module->create_loc_conf(cf);

if (ctx->loc_conf[mi] == NULL) {

return NGX_CONF_ERROR;

}

}

}

pcf = *cf;

cf->ctx = ctx; // 这里的ctx是ngx_http_conf_ctx_t类型的

for (m = 0; cf->cycle->modules[m]; m++) {

if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {

continue;

}

module = cf->cycle->modules[m]->ctx;

// 这里主要是调用各个模块的preconfiguration()方法进行前置配置,

// 可以看到,这里的preconfiguration()方法主要是在解析http模块之前进行调用的

if (module->preconfiguration) {

if (module->preconfiguration(cf) != NGX_OK) {

return NGX_CONF_ERROR;

}

}

}

cf->module_type = NGX_HTTP_MODULE;

cf->cmd_type = NGX_HTTP_MAIN_CONF;

// 由于这里是对http块进行解析,因而filename不需要传值,只需要根据当前位置开始进行解析即可。

// 需要注意的是,调用完此方法之后,各个配置项的初始化方法也就解析完成了

// 另外需要说明的是,当前解析的是http模块,调用到这里之后,就会对http模块的子模块进行解析,也就是说,

// 当前方法调用完成之后,server、location等模块都已经解析完成

rv = ngx_conf_parse(cf, NULL);

if (rv != NGX_CONF_OK) {

goto failed;

}

for (m = 0; cf->cycle->modules[m]; m++) {

if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {

continue;

}

// ngx_http_module_t

// mi记录了当前遍历的模块的配置的ctx_index,也即module参数创建的配置对应的位置

// 注意这里的module对象只是定义了当前模块进行相应的流程处理所需要执行的方法,其内部没有存储任何的与具体模块相关的数据对象

module = cf->cycle->modules[m]->ctx;

mi = cf->cycle->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;

}

}

// 这里主要是对各个模块的配置对象进行合并,因为有的配置项是既可以配置在http块,也可以配置在server块,

// 还可以配置在location块中的,这里会根据当前模块的要求,对相应的配置块进行合并

rv = ngx_http_merge_servers(cf, cmcf, module, mi);

if (rv != NGX_CONF_OK) {

goto failed;

}

}

// 省略部分代码...

// 调用各个模块的postconfiguration()方法进行配置解析完成之后的处理

for (m = 0; cf->cycle->modules[m]; m++) {

if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {

continue;

}

module = cf->cycle->modules[m]->ctx;

if (module->postconfiguration) {

if (module->postconfiguration(cf) != NGX_OK) {

return NGX_CONF_ERROR;

}

}

}

// 省略部分代码

return NGX_OK;

}

        这里展示的ngx_http_block()方法的源码我们只展示了与http模块结构体的初始化相关的代码,省略的代码我们将在后面的文章中进行讲解。ngx_http_block()方法可以说是解析http配置块的入口方法,该方法主要完成了如下:

  • 创建用于存储整个http模块配置数据的结构体ngx_http_conf_ctx_t
  • 计算当前所拥有的http模块的个数,并且会初始化各个模块中的ctx_index属性的值,将其标记为该模块在所有http模块中的相对位置,这个位置也就对应了当前模块在后面要讲解的main_confsrv_confloc_conf数组中的位置;
  • 分别为创建的ngx_http_conf_ctx_t结构体的main_confsrv_confloc_conf数组申请内存空间,而这些数组的长度就是前面计算得到的所有http模块的个数;
  • 遍历所有的模块,只要其为http模块,则依次调用其create_main_conf()create_srv_conf()create_loc_conf()方法,创建各个模块用于存储http配置块中的配置项结构体,需要注意的是,这些结构体的属性在此时还只是默认数据;
  • 遍历所有的http模块,调用其preconfiguration()方法进行配置文件解析之前的前置处理工作;
  • 调用ngx_conf_parse()方法,继续对http配置块进行解析,这里的解析工作其实就是对http配置块中的子配置项进行解析,不仅包括基础的配置项,也还包括server和location等配置块,在当前方法调用完成之后,整个http配置块都已经解析完成;
  • 遍历所有的http模块,调用其init_main_conf()方法,对各个模块的配置结构体属性设置默认值,由于上一步中已经解析完成了http配置块,因而配置文件中配置的属性是已经有值了的,这里的init_main_conf()方法主要是对那些没有配置的属性设置默认值。这里在初始化了各个模块的配置项时,还会调用ngx_http_merge_servers()进行配置项的合并,其主要作用就是在某个配置项在http块、server块或者location块中的两个及两个以上的配置块中都进行了配置时,将会选用哪一个值作为有效值的工作。这里的合并原理我们将在后面的文章进行讲解;
  • 遍历各个http模块,调用其postconfiguration()方法,进行配置解析完成之后的处理。

        上面的方法,可以说对解析http配置块的整套流程都进行了处理,这里的入口主要是进行了一些数据结构的初始化,而最终解析各个配置项的工作则主要交由ngx_conf_parse()方法进行。其中就包括server块和location块的解析。

2. server配置块的解析

        对于server块的解析,其对应的ngx_command_t结构体的配置如下:

{

ngx_string("server"),

NGX_HTTP_MAIN_CONF | NGX_CONF_BLOCK | NGX_CONF_NOARGS,

ngx_http_core_server,

0,

0,

NULL

}

        可以看到,对于server块的解析工作,主要是交由ngx_http_core_server()方法进行的。如下是该方法的源码:

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的类型为ngx_http_conf_ctx_t结构体,其存储了main_conf、srv_conf和loc_conf三个

// 属性的数组,这里需要特别注意,在ct->ctx也是一个ngx_http_conf_ctx_t类型的结构体,该属性在解析http

// 模块的时候已经进行了初始化,也就是说,这里的ctx->main_conf = http_ctx->main_conf;是将当前新创建的

// ctx的main_conf指向了其所在的http模块的main_conf,这样是可以达到一种复用的。

// 从这里可以看出,nginx在解析每个server块的时候,会每一个server块都新创建一个ngx_http_conf_t结构体,

// 用于存储当前正要解析的server块,而该结构体的main_conf会指向当前server块所在的http块的main_conf,

// 这样可以节省内存,而且这样做的另一个好处在于,后续进行server和http配置块的配置合并的时候,

// 只需要在每个server块对应的结构体中调用相应的合并方法即可

http_ctx = cf->ctx; // 存储http块的ngx_http_conf_t结构体

ctx->main_conf = http_ctx->main_conf;

// 为当前要解析的server块申请内存

ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);

if (ctx->srv_conf == NULL) {

return NGX_CONF_ERROR;

}

// 为location申请内存

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; cf->cycle->modules[i]; i++) {

if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {

continue;

}

module = cf->cycle->modules[i]->ctx;

// 调用各个模块的create_srv_conf()方法,以为每个模块创建其存储数据所需要的结构体对象

if (module->create_srv_conf) {

mconf = module->create_srv_conf(cf);

if (mconf == NULL) {

return NGX_CONF_ERROR;

}

ctx->srv_conf[cf->cycle->modules[i]->ctx_index] = mconf;

}

// 调用各个模块的create_loc_conf()方法,以为每个模块创建其存储数据所需要的结构体对象

if (module->create_loc_conf) {

mconf = module->create_loc_conf(cf);

if (mconf == NULL) {

return NGX_CONF_ERROR;

}

ctx->loc_conf[cf->cycle->modules[i]->ctx_index] = mconf;

}

}

// 这里的ngx_http_core_module就是server配置项所在的模块定义,这里就是获取该模块的一个配置对象,

// 也即ngx_http_core_srv_conf_t类型对象。

// 这里的ngx_http_core_module.ctx_index的值为0

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

cscf->ctx = ctx;

// 获取核心模块的配置对象,这里的main_conf上面只是将其指向了http块创建的main_conf数组,因而这里

// 获取到的也还是http模块中使用的同一个核心模块配置对象

cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];

// 将当前创建的

cscfp = ngx_array_push(&cmcf->servers);

if (cscfp == NULL) {

return NGX_CONF_ERROR;

}

*cscfp = cscf;

pcf = *cf;

cf->ctx = ctx;

cf->cmd_type = NGX_HTTP_SRV_CONF;

// 这里开始解析其余的配置项,比如location配置块

rv = ngx_conf_parse(cf, NULL);

*cf = pcf;

if (rv == NGX_CONF_OK && !cscf->listen) {

// 这里主要是创建一个ngx_http_listen_opt_t结构体,并且将各个属性进行初始化

ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));

sin = &lsopt.sockaddr.sockaddr_in;

sin->sin_family = AF_INET;

#if (NGX_WIN32)

sin->sin_port = htons(80);

#else

// 如果当前用户对80端口没有权限,则使用8000端口

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

#if (NGX_HAVE_TCP_FASTOPEN)

lsopt.fastopen = -1;

#endif

lsopt.wildcard = 1;

// 这里主要是将当前的地址和端口的类型将其组织到lsopt.sockaddr.sockaddr中

(void) ngx_sock_ntop(&lsopt.sockaddr.sockaddr, lsopt.socklen,

lsopt.addr, NGX_SOCKADDR_STRLEN, 1);

if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) {

return NGX_CONF_ERROR;

}

}

return rv;

}

        对于server配置块的解析,其主要分为如下几个步骤:

  • 创建用于存储SRV类型和LOC类型的模块的配置项的结构体数组,需要注意的是,这里并没有创建用于存储MAIN类型的模块的结构体数组,因为既然配置项是配置在server块中的,说明其一定不是MAIN类型的,因而这里只是将main_conf指向了http块的main_conf数组;
  • 依次调用各个http模块的create_srv_conf()方法和create_loc_conf()方法,创建这些模块用于存储SRV类型和LOC类型的配置结构体;
  • 找到解析http模块得到的配置数组中核心模块的配置结构体,将当前server块对应的ngx_http_core_srv_conf_t结构体添加到该核心模块的配置结构体的servers数组中,通过这种方式将http配置块解析得到的结构体与各个server块解析得到的结构体进行关联;
  • 调用ngx_conf_parse()方法继续解析当前server块下的子配置项,包括各个子location,当前方法调用完成后,表示当前server块的解析已经完成;
  • 将当前server块配置的需要监听的端口添加到已有的端口列表中,以便于后续进行监听;

3. location配置块的解析

        对于location配置块的解析,其处理过程与server块本质上是类似的,不过location配置块解析的过程中会处理location配置项后面所指定的url匹配规则,并且会判断当前的匹配规则是否使用了正则表达式。另外,location配置块解析的时候需要注意,location配置块内部是可以有多个子location配置块的,这些子location配置块相当于是对父location配置块配置的一个补充,至于子location配置块的层级有多少个,nginx并没有做出限制。如下是对location配置块进行定义的ngx_command_t结构体:

{

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

}

        可以看到,这里对location配置块的解析工作主要交由ngx_http_core_location()方法进行,如下是该方法的源码:

static char * ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy) {

char *rv;

u_char *mod;

size_t len;

ngx_str_t *value, *name;

ngx_uint_t i;

ngx_conf_t save;

ngx_http_module_t *module;

ngx_http_conf_ctx_t *ctx, *pctx;

ngx_http_core_loc_conf_t *clcf, *pclcf;

// 这里创建一个ctx对象,其主要是存储解析当前location配置块中的各个属性值

ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));

if (ctx == NULL) {

return NGX_CONF_ERROR;

}

pctx = cf->ctx;

// 这里将当前ctx的main_conf和srv_conf分别指向当前location块所在的server和http块对应的结构体对象

ctx->main_conf = pctx->main_conf;

ctx->srv_conf = pctx->srv_conf;

// 为loc_conf申请空间,以存储解析当前location块得到的各个模块属性值

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; cf->cycle->modules[i]; i++) {

if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {

continue;

}

module = cf->cycle->modules[i]->ctx;

// 如果当前模块的create_loc_conf()方法不为空,则调用该方法生成对应的配置

if (module->create_loc_conf) {

ctx->loc_conf[cf->cycle->modules[i]->ctx_index] = module->create_loc_conf(cf);

if (ctx->loc_conf[cf->cycle->modules[i]->ctx_index] == NULL) {

return NGX_CONF_ERROR;

}

}

}

// 获取解析得到的核心模块的loc_conf值

clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];

// 这里其实就是将核心模块的loc_conf指向当前location块对应的loc_conf值

clcf->loc_conf = ctx->loc_conf;

// 这里的elts是location配置块的参数个数,比如

// location / {

// root html;

// }

// 那么这里的参数个数就是2,分别为location和/

// 这里我们首先需要说明一下location的语法:

// - 语法:

// location [=|~|~*|^~] uri {...}

// location @name {...}

//- 使用规则:

// - 前缀字符串匹配:

// - 常规的字符串,比如`/html`,那么就匹配前缀为`/html`的URI;

// - `=`:精确匹配,那么URI就必须等于后面的字符串;

// - `^~`:如果匹配上了,就不再进行正则表达式匹配;

// - 正则表达式匹配:

// - `~`:大小写敏感的正则匹配;

// - `~*`:忽略大小写的正则匹配;

// - 用于内部跳转的命名location:

// - `@`

// - 合并连续的`/`符号:

// - `merge_slashes on;`

value = cf->args->elts;

// 判断当前的参数个数是否为3

if (cf->args->nelts == 3) {

// 这里下标为1的参数即为第二个参数

len = value[1].len;

mod = value[1].data;

name = &value[2];

if (len == 1 && mod[0] == "=") {

// 这里的exact_match字段标志了当前匹配方式是否为完全匹配,

// 由于当前是=操作,因而将其标志为1

clcf->name = *name;

clcf->exact_match = 1;

// 如果当前是^~,则不进行正则匹配

} else if (len == 2 && mod[0] == "^" && mod[1] == "~") {

clcf->name = *name;

clcf->noregex = 1;

// 如果当前是~,则表示当前是大小写敏感的正则匹配

} else if (len == 1 && mod[0] == "~") {

if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {

return NGX_CONF_ERROR;

}

// 如果当前是~*,则表示其为大小写不敏感的正则匹配

} else if (len == 2 && mod[0] == "~" && mod[1] == "*") {

if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {

return NGX_CONF_ERROR;

}

} else {

ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

"invalid location modifier "%V"", &value[1]);

return NGX_CONF_ERROR;

}

} else {

// 这里else分支的功能与上面if分支的功能基本上是一致的,主要区别在于下面允许第二个和第三个元素之间是

// 没有空格的,除此之外,下面的else分支还处理了一种情况就是location后的参数是以@符号开头的,

// 这种情况是会转发当前的请求到其他的location中的

name = &value[1];

if (name->data[0] == "=") {

clcf->name.len = name->len - 1;

clcf->name.data = name->data + 1;

clcf->exact_match = 1;

} else if (name->data[0] == "^" && name->data[1] == "~") {

clcf->name.len = name->len - 2;

clcf->name.data = name->data + 2;

clcf->noregex = 1;

} else if (name->data[0] == "~") {

name->len--;

name->data++;

if (name->data[0] == "*") {

name->len--;

name->data++;

if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {

return NGX_CONF_ERROR;

}

} else {

if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {

return NGX_CONF_ERROR;

}

}

} else {

clcf->name = *name;

// 处理以@符号开头的情况

if (name->data[0] == "@") {

clcf->named = 1;

}

}

}

// 如果当前location是嵌入某个location中的,那么pclcf指向的就是父location的解析值,

// 如果没有父location,那么这里的cf->cmd_type就不等于NGX_HTTP_LOC_CONF

pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];

if (cf->cmd_type == NGX_HTTP_LOC_CONF) {

/* nested location */

#if 0

clcf->prev_location = pclcf;

#endif

// 如果父location是精确匹配,那么其是不能有子location的

if (pclcf->exact_match) {

ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

"location "%V" cannot be inside "

"the exact location "%V"",

&clcf->name, &pclcf->name);

return NGX_CONF_ERROR;

}

// 如果父location使用了@进行重定向,那么其是不能有子location的

if (pclcf->named) {

ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

"location "%V" cannot be inside "

"the named location "%V"",

&clcf->name, &pclcf->name);

return NGX_CONF_ERROR;

}

// 使用@进行重定向的location只允许出现在server块下,不能出现在嵌套的子location中

if (clcf->named) {

ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

"named location "%V" can be "

"on the server level only",

&clcf->name);

return NGX_CONF_ERROR;

}

len = pclcf->name.len;

#if (NGX_PCRE)

// 这里的len是父location的表达式的长度,因而这里就是比较子location是否是以父location为前缀开头的,

// 如果不是,则返回异常

if (clcf->regex == NULL && ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)

#else

if (ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)

#endif

{

ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,

"location "%V" is outside location "%V"",

&clcf->name, &pclcf->name);

return NGX_CONF_ERROR;

}

}

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配置块的解析工作主要分为如下几个步骤进行:

  • 申请用于存储location配置块中各个模块的配置项的配置的结构体数组,并将其保存到loc_conf属性中,而其main_confsrv_conf则直接继承自当前location块所在的server块对应的结构体的main_confsrv_conf
  • 依次调用各个http模块的create_loc_conf()方法,创建用于存储各个模块的配置项的结构体;
  • 解析location配置项后面的用于匹配url的表达式;
  • 进行父子location的校验工作,比如,父location是精确匹配的,那么此时是不能有子location的。这项校验工作主要用于有嵌套location的情况;
  • 继续调用ngx_conf_parse()方法解析当前location配置块下的子配置项,也包括子location的解析。可以看出来,这里对子location的解析过程实际上就是一个递归解析的过程,而解析所使用的方法还是上面的ngx_http_core_location()方法。

4. 小结

        本文主要从源码的角度对nginx是如何解析http块、server块和location块的过程进行了详细讲解,着重说明了其是如何构建nginx http模块的配置结构体的。

以上是 nginxhttp块配置解析 的全部内容, 来源链接: utcz.com/z/513562.html

回到顶部