4.3 HTTP配置模型

上文中我们了解了如何使用Nginx提供的预设解析方法来处理自己感兴趣的配置项,由于http配置项设计得有些复杂,为了更清晰地使用好ngx_command_t结构体处理http配置项,本节将简单讨论HTTP配置模型是怎样实现的,在第10章我们会从HTTP框架的角度谈谈它是怎么管理每一个HTTP模块的配置结构体的。

当Nginx检测到http{……}这个关键配置项时,HTTP配置模型就启动了,这时会首先建立1个ngx_http_conf_ctx_t结构。下面看一下ngx_http_conf_ctx_t的定义。


typedef struct{

/指针数组,数组中的每个元素指向所有HTTP模块create_main_conf方法产生的结构体/

void**main_conf;

/指针数组,数组中的每个元素指向所有HTTP模块create_srv_conf方法产生的结构体/

void**srv_conf;

/指针数组,数组中的每个元素指向所有HTTP模块create_loc_conf方法产生的结构体/

void**loc_conf;

}ngx_http_conf_ctx_t;


这时,HTTP框架会为所有的HTTP模块建立3个数组,分别存放所有HTTP模块的create_main_conf、create_srv_conf、create_loc_conf方法返回的地址指针(就像本章的例子中mytest模块在create_loc_conf中生成了ngx_http_mytest_conf_t结构,并在create_loc_conf方法返回时将指针传递给HTTP框架)。当然,如果HTTP模块对于配置项不感兴趣,它没有实现create_main_conf、create_srv_conf、create_loc_conf等方法,那么数组中相应位置存储的指针是NULL。ngx_http_conf_ctx_t的3个成员main_conf、srv_conf、loc_conf分别指向这3个数组。下面看一段简化的代码,了解如何设置create_loc_conf返回的地址。


ngx_http_conf_ctx_t*ctx;

//HTTP框架生成了1个ngx_http_conf_ctx_t结构

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

if(ctx==NULL){

return NGX_CONF_ERROR;

}

//生成1个数组存储所有的HTTP模块create_loc_conf方法返回的地址

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

if(ctx->loc_conf==NULL){

return NGX_CONF_ERROR;

}

//遍历所有的HTTP模块

for(m=0;ngx_modules[m];m++){

if(ngx_modules[m]->type!=NGX_HTTP_MODULE){

continue;

}

module=ngx_modules[m]->ctx;

mi=ngx_modules[m]->ctx_index;

/如果这个HTTP模块实现了create_loc_conf,就调用它,并把返回的地址存储到loc_conf中/

if(module->create_loc_conf){

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

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

return NGX_CONF_ERROR;

}

}

}


这样,在http{……}块中就通过1个ngx_http_conf_ctx_t结构保存了所有HTTP模块的配置数据结构的入口。以后遇到任何server{……}块或者location{……}块时,也会建立ngx_http_conf_ctx_t结构,生成同样的数组来保存所有HTTP模块通过create_srv_conf、create_loc_conf等方法返回的指针地址。ngx_http_conf_ctx_t是了解http配置块的基础,下面我们来看看具体的解析流程。

4.3.1 解析HTTP配置的流程

图4-1是HTTP框架解析配置项的示意流程图(图中出现了ngx_http_module和ngx_http_core_module模块,所谓的HTTP框架主要由这两个模块组成),下面解释图中每个流程的意义。

4.3 HTTP配置模型 - 图1

图 4-1 解析http配置项的示意流程图

1)图4-1中的主循环是指Nginx进程的主循环,主循环只有调用配置文件解析器才能解析nginx.conf文件(这里的“主循环”是指解析全部配置文件的循环代码,图8-6的第4步,为了便于理解,可以认为是Nginx框架代码在循环解析配置项)。

2)当发现配置文件中含有http{}关键字时,HTTP框架开始启动,这一过程详见10.7节描述的ngx_http_block方法。

3)HTTP框架会初始化所有HTTP模块的序列号,并创建3个数组用于存储所有HTTP模块的create_main_conf、create_srv_conf、create_loc_conf方法返回的指针地址,并把这3个数组的地址保存到ngx_http_conf_ctx_t结构中。

4)调用每个HTTP模块(当然也包括例子中的mytest模块)的create_main_conf、create_srv_conf、create_loc_conf(如果实现的话)方法。

5)把各HTTP模块上述3个方法返回的地址依次保存到ngx_http_conf_ctx_t结构体的3个数组中。

6)调用每个HTTP模块的preconfiguration方法(如果实现的话)。

7)注意,如果preconfiguration返回失败,那么Nginx进程将会停止。

8)HTTP框架开始循环解析nginx.conf文件中http{……}里面的所有配置项,注意,这个过程到第19步才会返回。

9)配置文件解析器在检测到1个配置项后,会遍历所有的HTTP模块,检查它们的ngx_command_t数组中的name项是否与配置项名相同。

10)如果找到有1个HTTP模块(如mytest模块)对这个配置项感兴趣(如test_myconfig配置项),就调用ngx_command_t结构中的set方法来处理。

11)set方法返回是否处理成功。如果处理失败,那么Nginx进程会停止。

12)配置文件解析器继续检测配置项。如果发现server{……}配置项,就会调用ngx_http_core_module模块来处理。因为ngx_http_core_module模块明确表示希望处理server{}块下的配置项。注意,这次调用到第18步才会返回。

13)ngx_http_core_module模块在解析server{……}之前,也会如第3步一样建立ngx_http_conf_ctx_t结构,并建立数组保存所有HTTP模块返回的指针地址。然后,它会调用每个HTTP模块的create_srv_conf、create_loc_conf方法(如果实现的话)。

14)将上一步各HTTP模块返回的指针地址保存到ngx_http_conf_ctx_t对应的数组中。

15)开始调用配置文件解析器来处理server{……}里面的配置项,注意,这个过程在第17步返回。

16)继续重复第9步的过程,遍历nginx.conf中当前server{……}内的所有配置项。

17)配置文件解析器继续解析配置项,发现当前server块已经遍历到尾部,说明server块内的配置项处理完毕,返回ngx_http_core_module模块。

18)http core模块也处理完server配置项了,返回至配置文件解析器继续解析后面的配置项。

19)配置文件解析器继续解析配置项,这时发现处理到了http{……}的尾部,返回给HTTP框架继续处理。

20)在第3步和第13步,以及我们没有列出来的某些步骤中(如发现其他server块或者location块),都创建了ngx_http_conf_ctx_t结构,这时将开始调用merge_srv_conf、merge_loc_conf等方法合并这些不同块(http、server、location)中每个HTTP模块分配的数据结构。

21)HTTP框架处理完毕http配置项(也就是ngx_command_t结构中的set回调方法处理完毕),返回给配置文件解析器继续处理其他http{……}外的配置项。

22)配置文件解析器处理完所有配置项后会告诉Nginx主循环配置项解析完毕,这时Nginx才会启动Web服务器。

注意 图4-1并没有列出解析location{……}块的流程,实际上,解析location与解析server并没有本质上的区别,为了简化起见,没有把它画到图中。