Nginx HTTP模块的配置项管理【2】
来源:互联网 发布:经典的心理学书籍知乎 编辑:程序博客网 时间:2024/06/15 06:28
本篇文章将具体介绍一下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这些成员将所有配置项关联起来。
- Nginx HTTP模块的配置项管理
- Nginx HTTP模块的配置项管理【2】
- nginx http模块中配置的实现和解析---1
- 架构师日记——Nginx的HTTP模块配置
- Nginx源码剖析--HTTP模块配置信息的merge
- nginx http proxy模块缓冲区管理
- Nginx的HTTP Access模块
- nginx的HTTP模块编写
- nginx-http模块的数据结构
- nginx的HTTP模块编写
- nginx配置详解之http模块
- Nginx安装HTTP SSL模块基本配置
- Nginx模块-简单的HTTP模块
- Nginx的配置与部署(9)核心模块之HTTP模块基本常用指令
- Nginx的配置与部署(10)核心模块之HTTP模块Location相关指令
- nginx学习笔记(2):开发一个简单的HTTP模块
- 13 Nginx的标准http模块
- 【Nginx】开发一个简单的HTTP模块
- 一个华为人辞职创业后的几个反思
- 安装K/3 WISE,中间层服务器与数据库服务器分开安装注意事项
- 国内著名IT公司(百度、搜狗、网易、新浪)2012校园招聘笔试、面试小结
- NSLog使用,在release版本禁止输出NSLog内容
- 发现编程并非适合每个人,有耐心才行
- Nginx HTTP模块的配置项管理【2】
- nyoj 士兵杀敌(二)
- [百度笔试]百度笔试大集锦
- The connection to adb is down, and a severe error has occured(Android模拟器端口被占用)
- leetcode A String Replacement Problem---流程图
- poj 2481 Cows
- PostgreSQL学习手册(函数和操作符<二>)
- 微信公众号
- Java Visitor 模式