4.2.2 设定配置项的解析方式

下面详细介绍在读取HTTP配置时是如何使用ngx_command_t结构的,首先回顾一下第3章中曾经提到过的定义,再详细介绍每个成员的意义。


struct ngx_command_s{

ngx_str_t name;

ngx_uint_t type;

charset)(ngx_conf_tcf,ngx_command_tcmd,void*conf);

ngx_uint_t conf;

ngx_uint_t offset;

void*post;

};


(1)ngx_str_t name

其中,name是配置项名称,如4.1节例子中的"test_str"。

(2)ngx_uint_t type

其中,type决定这个配置项可以在哪些块(如http、server、location、if、upstream块等)中出现,以及可以携带的参数类型和个数等。表4-1列出了设置http配置项时type可以取的值。注意,type可以同时取表4-1中的多个值,各值之间用|符号连接,例如,type可以取值为NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1。

4.2.2 设定配置项的解析方式 - 图1

4.2.2 设定配置项的解析方式 - 图2

注意 如果HTTP模块中定义的配置项在nginx.conf配置文件中实际出现的位置和参数格式与type的意义不符,那么Nginx在启动时会报错。

(3)charset)(ngx_conf_tcf,ngx_command_tcmd,void*conf)

关于set回调方法,在第3章中处理mytest配置项时已经使用过,其中mytest配置项是不带参数的。如果处理配置项,我们既可以自己实现一个回调方法来处理配置项(4.2.4节中会举例说明如何自定义回调方法),也可以使用Nginx预设的14个解析配置项方法,这会少写许多代码,表4-2列出了这些预设的解析配置项方法。我们将在4.2.3节中举例说明这些预设方法的使用方式。

4.2.2 设定配置项的解析方式 - 图3

4.2.2 设定配置项的解析方式 - 图4

(4)ngx_uint_t conf

conf用于指示配置项所处内存的相对偏移位置,仅在type中没有设置NGX_DIRECT_CONF和NGX_MAIN_CONF时才会生效。对于HTTP模块,conf是必须要设置的,它的取值范围见表4-3。

4.2.2 设定配置项的解析方式 - 图5

为什么HTTP模块一定要设置conf的值呢?因为HTTP框架可以使用预设的14种方法自动地将解析出的配置项写入HTTP模块代码定义的结构体中,但HTTP模块中可能会定义3个结构体,分别用于存储main、srv、loc级别的配置项(对应于create_main_conf、create_srv_conf、create_loc_conf方法创建的结构体),而HTTP框架自动解析时需要知道应把解析出的配置项值写入哪个结构体中,这将由conf成员完成。

因此,对conf的设置是与ngx_http_module_t实现的回调方法(在4.2.1节中介绍)相关的。如果用于存储这个配置项的数据结构是由create_main_conf回调方法完成的,那么必须把conf设置为NGX_HTTP_MAIN_CONF_OFFSET。同样,如果这个配置项所属的数据结构是由create_srv_conf回调方法完成的,那么必须把conf设置为NGX_HTTP_SRV_CONF_OFFSET。可如果create_loc_conf负责生成存储这个配置项的数据结构,就得将conf设置为NGX_HTTP_LOC_CONF_OFFSET。

目前,功能较为简单的HTTP模块都只实现了create_loc_conf回调方法,对于http{}、server{}块内出现的同名配置项,都是并入某个location{}内create_loc_conf方法产生的结构体中的(在4.2.5节中会详述如何合并配置项)。当我们希望同时出现在http{}、server{}、location{}块的同名配置项,在HTTP模块的代码中保存于不同的变量中时,就需要实现create_main_conf方法、create_srv_conf方法产生新的结构体,从而以不同的结构体独立保存不同级别的配置项,而不是全部合并到某个location下create_loc_conf方法生成的结构体中。

(5)ngx_uint_t offset

offset表示当前配置项在整个存储配置项的结构体中的偏移位置(以字节(Byte)为单位)。举个例子,在32位机器上,int(整型)类型长度是4字节,那么看下面这个数据结构:


typedef struct{

int a;

int b;

int c;

}test_stru;


如果要处理的配置项是由成员b来存储参数的,那么这时b相对于test_stru的偏移量就是4;如果要处理的配置项由成员c来存储参数,那么这时c相对于test_stru的偏移量就是8。

实际上,这种计算工作不用用户自己来做,使用offsetof宏即可实现。例如,在上例中取b的偏移量时可以这么做:


offsetof(test_stru,b)


其中,offsetof中第1个参数是存储配置项的结构体名称,第2个参数是这个结构体中的变量名称。offsetof将会返回这个变量相对于结构体的偏移量。

提示 offsetof这个宏是如何取得成员相对结构体的偏移量的呢?其实很简单,它的实现类似于:#define offsetof(type,member)(size_t)&(((type*)0)->member)。可以看到,offsetof将0地址转换成type结构体类型的指针,并在访问member成员时取得member成员的指针,这个指针相对于0地址来说自然就是成员相对于结构体的偏移量了。

设置offset有什么作用呢?如果使用Nginx预设的解析配置项方法,就必须设置offset,这样Nginx首先通过conf成员找到应该用哪个结构体来存放,然后通过offset成员找到这个结构体中的相应成员,以便存放该配置。如果是自定义的专用配置项解析方法(只解析某一个配置项),则可以不设置offset的值。读者可以通过4.3.4节来了解预设配置项解析方法是如何使用offset的。

(6)void*post

post指针有许多用途,从它被设计成void*就可以看出。

如果自定义了配置项的回调方法,那么post指针的用途完全由用户来定义。如果不使用它,那么随意设为NULL即可。如果想将一些数据结构或者方法的指针传过来,那么使用post也可以。

如果使用Nginx预设的配置项解析方法,就需要根据这些预设方法来决定post的使用方式。表4-4说明了post相对于14个预设方法的用途。

4.2.2 设定配置项的解析方式 - 图6

可以看到,有9个预设方法在使用时post是可以设置为ngx_conf_post_t结构体来使用的,先来看看ngx_conf_post_t的定义。


typedef charngx_conf_post_handler_pt)(ngx_conf_t*cf,

voiddata,voidconf);

typedef struct{

ngx_conf_post_handler_pt post_handler;

}ngx_conf_post_t;


如果需要在解析完配置项(表4-4中列出的前9个预设方法)后回调某个方法,就要实现上面的ngx_conf_post_handler_pt,并将包含post_handler的ngx_conf_post_t结构体传给post指针。

目前,ngx_conf_post_t结构体提供的这个功能没有官方Nginx模块使用,因为它限制过多且post成员过于灵活,一般完全可以init_main_conf这样的方法统一处理解析完的配置项。