10.2 管理HTTP模块的配置项

上文介绍过事件配置项的管理,其实HTTP配置项的管理与事件模块有些相似,但由于它具有3种不同级别配置项,所以管理要复杂许多。对于HTTP模块而言,只需关心工作时能够取到正确的配置项。但对于HTTP框架而言,任何一个HTTP模块的server相关的配置项都是可能出现在main级别中,而location相关的配置项可能出现在main、srv级别中。而server是可能存在许多个的,location更是可以反复嵌套的,这样就要为每个HTTP模块按照nginx.conf里的配置块建立许多份配置。在10.1节的例子中,共出现了7个配置块,对于HTTP框架而言,就需要为所有的HTTP模块分配7个用于存储配置结构体指针的数组。

在处理http{}块内的main级别配置项时,对每个HTTP模块来说,都会调用create_main_conf、create_srv_conf、create_loc_conf方法建立3个结构体,分别用于存储HTTP全局配置项、server配置项、location配置项。现在问题来了,http{}内的配置项明明就是main级别的,有了create_main_conf生成的结构体已经足够保存全局配置项参数了,为什么还要调用create_srv_conf、create_loc_conf方法建立结构体呢?其实,这是为了把同时出现在http{}、server{}、location{}内的相同配置项进行合并而做的准备。假设有一个与server相关的配置项(例如负责指定每个TCP连接上内存池大小的connection_pool_size配置项)同时出现在http{}、server{}中,那么对它感兴趣的HTTP模块就有权决定srv结构体内的成员究竟是以main级别配置项为准,还是srv级别配置项为准。结合10.1节的例子来看,mytest_num出现在http{}下时参数为1,出现在server A{}下时参数为2,那么,mytest模块就有权决定,当处理server A虚拟主机时,究竟是把mytest_num参数当做1还是2,或者把它们俩相加,这都是任何一个HTTP模块的自由。对于HTTP框架而言,在解析main级别的配置项时,必须同时创建3个结构体,用于合并之后会解析到的server、location相关的配置项。

对于server{}块内配置项的处理,需要调用每个HTTP模块的create_srv_conf方法、create_loc_conf方法建立两个结构体,分别用于存储server、location相关的配置项,其中create_loc_conf产生的结构体仅用于合并location相关的配置项。

对于location块内配置项的处理则简单许多,只需要调用每个HTTP模块的create_loc_conf方法建立1个结构体即可。

结合10.1节中nginx.conf配置文件的片断来看,实际上HTTP框架最多必须为一个HTTP模块(如mytest模块)创建3+2+1+1+2+1+1=11个配置结构体,而经过合并后实际上每个HTTP模块会用到的结构体有7个。可为什么mytest模块使用ngx_http_mytest_conf_t结构体时好像只有1个配置结构体呢?因为在HTTP框架处理到某个阶段时,例如,在寻找到适合的location前,如果试图去取ngx_http_mytest_conf_t结构体,得到的将是srv级别下的配置,而寻找到location后,ngx_http_mytest_conf_t结构体中的成员将是loc级别下的配置。

下面介绍一下ngx_http_module模块在实现上是如何体现上述思路的。

10.2.1 管理main级别下的配置项

上文说过,在解析HTTP模块定义的main级别配置项时,将会分别调用每个HTTP模块的create_main_conf、create_srv_conf、create_loc_conf方法建立3个结构体,分别用于存储全局、server相关的、location相关的配置项,但它们究竟是以何种数据结构保存的呢?与核心结构体ngx_cycle_t中的conf_ctx指针又有什么样的关系呢?在图10-10中的第2步~第7步包含了解析main级别配置项的所有流程,而图10-1将会展现它们在内存中的布局,可以看到,其中ngx_http_core_module模块完成了HTTP框架的大部分功能,而它又是第1个HTTP模块,因此,它使用到的3个结构体(ngx_http_core_main_conf_t、ngx_http_core_srv_conf_t、ngx_http_core_loc_conf_t)也是用户非常关心的。

图10-1中有一个结构体叫做ngx_http_conf_ctx_t,它是HTTP框架中一个经常用到的数据结构,下面看看它的定义。

10.2.1 管理main级别下的配置项 - 图1

图 10-1 HTTP框架解析main级别配置项时配置结构体的内存示意图


typedef struct{

/指向一个指针数组,数组中的每个成员都是由所有HTTP模块的create_main_conf方法创建的存放全局配置项的结构体,它们存放着解析直属http{}块内的main级别的配置项参数/

void**main_conf;

/指向一个指针数组,数组中的每个成员都是由所有HTTP模块的create_srv_conf方法创建的与server相关的结构体,它们或存放main级别配置项,或存放srv级别配置项,这与当前的ngx_http_conf_ctx_t是在解析http{}或者server{}块时创建的有关/

void**srv_conf;

/指向一个指针数组,数组中的每个成员都是由所有HTTP模块的create_loc_conf方法创建的与location相关的结构体,它们可能存放着main、srv、loc级别的配置项,这与当前的ngx_http_conf_ctx_t是在解析http{}、server{}或者location{}块时创建的有关/

void**loc_conf;

}ngx_http_conf_ctx_t;


ngx_http_conf_ctx_t中仅有3个成员,它们分别指向3个指针数组。在10.2.4节中,读者会看到srv_conf数组和loc_conf数组在配置项的合并操作中是如何使用的。

在核心结构体ngx_cycle_t的conf_ctx成员指向的指针数组中,第7个指针由ngx_http_module模块使用(ngx_http_module模块的index序号为6,由于由0开始,所以它在ngx_modules数组中排行第7。在存放全局配置结构体的conf_ctx数组中,第7个成员指向ngx_http_module模块),这个指针设置为指向解析http{}块时生成的ngx_http_conf_ctx_t结构体,而ngx_http_conf_ctx_t的3个成员则分别指向新分配的3个指针数组。新的指针数组中成员的意义由每个HTTP模块的ctx_index序号指定(ctx_index在HTTP模块中表明它处于HTTP模块间的序号),例如,第6个HTTP模块的ctx_index是5(ctx_index同样由0开始计数),那么在ngx_http_conf_ctx_t的3个数组中,第6个成员就指向第6个HTTP模块的create_main_conf、create_srv_conf、create_loc_conf方法建立的结构体,当然,如果相应的回调方法没有实现,该指针就为NULL空指针。

ngx_http_core_module模块是第1个HTTP模块,它的ctx_index序号是0,因此,数组中的第1个指针将指向ngx_http_core_module模块生成的ngx_http_core_main_conf_t、ngx_http_core_srv_conf_t、ngx_http_core_loc_conf_t结构体。

可如何由ngx_cycle_t核心结构体中找到main级别的配置结构体呢?Nginx提供的ngx_http_cycle_get_module_main_conf宏可以实现这个功能,如下所示。


define ngx_http_cycle_get_module_main_conf(cycle,module)\

(cycle->conf_ctx[ngx_http_module.index]?\

((ngx_http_conf_ctx_t*)

cycle->conf_ctx[ngx_http_module.index])

->main_conf[module.ctx_index]:\

NULL)


其中参数cycle是ngx_cycle_t核心结构体指针,而module则是所要操作的HTTP模块。它的实现很简单,先由cycle的conf_ctx指针数组中找到ngx_http_module.index序号(上文说过,其index为6)对应的指针,获取到http{}块下的ngx_http_conf_ctx_t成员,然后经由main_conf数组即可找到所有HTTP模块的main级别配置结构体。最后,根据所要查询的module数组的ctx_index序号取得其main级别下的配置结构体,例如:


ngx_http_perl_main_conf_t*pmcf=ngx_http_cycle_get_module_main_conf(cycle,ngx_http_perl_module);


注意 HTTP全局配置项是基础,管理server、location等配置块时取决于ngx_http_core_module模块出现在main级别下存储全局配置项的ngx_http_core_main_conf_t结构体。