4.2 怎样使用http配置
事实上,在第3章中已经使用过mytest配置项,只不过当时mytest配置项是没有值的,只是用来标识当location块内出现mytest配置项时就启用mytest模块,从而处理匹配该location表达式的用户请求。本章将由易到难来阐述HTTP模块是怎样获得感兴趣的配置项的。
处理http配置项可以分为下面4个步骤:
1)创建数据结构用于存储配置项对应的参数。
2)设定配置项在nginx.conf中出现时的限制条件与回调方法。
3)实现第2步中的回调方法,或者使用Nginx框架预设的14个回调方法。
4)合并不同级别的配置块中出现的同名配置项。
不过,这4个步骤如何与Nginx有机地结合起来呢?就是通过第3章中介绍过的两个数据结构ngx_http_module_t和ngx_command_t,它们都是定义一个HTTP模块时不可或缺的部分。
4.2.1 分配用于保存配置参数的数据结构
首先需要创建一个结构体,其中包含了所有我们感兴趣的参数。为了说明14种预设配置项的解析方法,我们将在这个结构体中定义14个成员,存储感兴趣的配置项参数。例如:
typedef struct{
ngx_str_t
my_str;
ngx_int_t
my_num;
ngx_flag_t
my_flag;
size_t
my_size;
ngx_array_t*my_str_array;
ngx_array_t*my_keyval;
off_t
my_off;
ngx_msec_t
my_msec;
time_t
my_sec;
ngx_bufs_t
my_bufs;
ngx_uint_t
my_enum_seq;
ngx_uint_t
my_bitmask;
ngx_uint_t
my_access;
ngx_path_t*my_path;
}ngx_http_mytest_conf_t;
ngx_http_mytest_conf_t中的14个成员存储的配置项都不相同,读者可暂时忽略上面ngx_http_mytest_conf_t结构中一些没见过的Nginx数据结构,这些将在4.2.3节中介绍。
为什么要这么严格地用一个结构体来存储配置项的参数值,而不是随意地定义几个全局变量来存储它们呢?这就要回到4.1节中例子的使用场景了,多个location块(或者http块、server块)中的相同配置项是允许同时生效的,也就是说,我们刚刚定义的ngx_http_mytest_conf_t结构必须在Nginx的内存中保存许多份。事实上,HTTP框架在解析nginx.conf文件时只要遇到http{}、server{}或者location{}配置块就会立刻分配一个新的ngx_http_mytest_conf_t结构体。因此,HTTP模块感兴趣的配置项需要统一地使用一个struct结构体来保存(否则HTTP框架无法管理),如果nginx.conf文件中在http{}下有多个server{}或者location{},那么这个struct结构体在Nginx进程中就会存在多份实例。
Nginx怎样管理我们自定义的存储配置的结构体ngx_http_mytest_conf_t呢?很简单,通过第3章中曾经提到的ngx_http_module_t中的回调方法。下面回顾一下ngx_http_module_t的定义。
typedef struct{
ngx_int_t(preconfiguration)(ngx_conf_tcf);
ngx_int_t(postconfiguration)(ngx_conf_tcf);
void(create_main_conf)(ngx_conf_t*cf);
char(init_main_conf)(ngx_conf_tcf,voidconf);
void(create_srv_conf)(ngx_conf_t*cf);
char(merge_srv_conf)(ngx_conf_tcf,voidprev,void*conf);
void(create_loc_conf)(ngx_conf_t*cf);
char(merge_loc_conf)(ngx_conf_tcf,voidprev,void*conf);}ngx_http_module_t;
其中,create_main_conf、create_srv_conf、create_loc_conf这3个回调方法负责把我们分配的用于保存配置项的结构体传递给HTTP框架。下面解释一下为什么不是定义1个而是定义3个回调方法。
HTTP框架定义了3个级别的配置main、srv、loc,分别表示直接出现在http{}、server{}、location{}块内的配置项。当nginx.conf中出现http{}时,HTTP框架会接管配置文件中http{}块内的配置项解析,之后的流程可以由4.3.1节中的图4-1来了解。当遇到http{……}配置块时,HTTP框架会调用所有HTTP模块可能实现的create_main_conf、create_srv_conf、create_loc_conf方法生成存储main级别配置参数的结构体;在遇到server{……}块时会再次调用所有HTTP模块的create_srv_conf、create_loc_conf回调方法生成存储srv级别配置参数的结构体;在遇到location{……}时则会再次调用create_loc_conf回调方法生成存储loc级别配置参数的结构体。因此,实现这3个回调方法的意义是不同的,例如,对于mytest模块来说,在http{}块内只会调用1次create_main_conf,而create_loc_conf可能会被调用许多次,也就是有许多个由create_loc_conf生成的结构体。
普通的HTTP模块往往只实现create_loc_conf回调方法,因为它们只关注匹配某种URL的请求。我们的mytest例子也是这样实现的,这里实现create_loc_conf的是ngx_http_mytest_create_loc_conf方法,如下所示。
static voidngx_http_mytest_create_loc_conf(ngx_conf_tcf)
{
ngx_http_mytest_conf_t*mycf;
mycf=(ngx_http_mytest_conf_t*)ngx_pcalloc(cf->pool,sizeof(ngx_http_mytest_conf_t));
if(mycf==NULL){
return NULL;
}
mycf->test_flag=NGX_CONF_UNSET;
mycf->test_num=NGX_CONF_UNSET;
mycf->test_str_array=NGX_CONF_UNSET_PTR;
mycf->test_keyval=NULL;
mycf->test_off=NGX_CONF_UNSET;
mycf->test_msec=NGX_CONF_UNSET_MSEC;
mycf->test_sec=NGX_CONF_UNSET;
mycf->test_size=NGX_CONF_UNSET_SIZE;
return mycf;
}
上述代码中对一些配置参数设置了初始值,这是为了14个预设方法准备的,下面会解释为什么要这样赋值。