Nginx HTTP模块的配置项管理【2】
2014-12-28 18:03
162 查看
本篇文章将具体介绍一下Nginx解析HTTP模块配置文件的流程。
核心模块ngx_http_module是进入HTTP模块解析的入口。该模块仅仅定义了一个ngx_command_t结构体,用于声明遇到http{}时需要进入的处理函数
而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,我大致列举了几个:
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函数解析。
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这些成员将所有配置项关联起来。
核心模块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这些成员将所有配置项关联起来。
相关文章推荐
- Nginx HTTP模块的配置项管理
- nginx数据推送模块 - nginx http push module
- nginx 整合 ngx_http_accesskey_module 模块的应用
- nginx源码分析(2)——http模块的初始化过程
- 详细解释:nginx中ngx_http_auth_basic_module模块(HTTP Auth Basic 模块)配置及各个参数含义 .
- 详细解释:nginx中ngx_http_access_module模块(HTTP Access 模块)配置及各个参数含义
- nginx 动态缩略图模块安装(ngx_http_image_filter_module)
- Nginx Http模块开发
- nginx模块学习——nginx_http_push_module模块深入讲解和聊天室实现
- 解剖Nginx·模块开发篇(5)解读内置非默认模块 ngx_http_stub_status_module
- nginx图片过滤处理模块http_image_filter_module安装配置笔记
- Nginx服务器工作状态ngx_http_stub_status_module 模块
- 高性能Web服务器Nginx的配置与部署研究(10)核心模块之HTTP模块Location相关指令
- 高性能Web服务器Nginx的配置与部署研究(9)核心模块之HTTP模块基本常用指令
- nginx模块学习——nginx_http_push_module模块深入讲解和聊天室实现
- Nginx Http模块开发
- nginx版本变化引起的自定义http模块的运行异常
- 高性能Web服务器Nginx的配置与部署研究(9)核心模块之HTTP模块基本常用指令
- 详细解释:nginx中ChsHttpProxyModule模块各项配置及其含义
- Nginx的HTTP Access模块