【Nginx】如何应对HTTP组态
相同的配置项可以在相同的时间内发生的多个块。例如HTTP片、server片、location片。阻断取之于在配置项值。
处理HTTP配置项分下面4个步骤:
- 创建数据结构用于存储配置项相应的參数
- 设定配置项在配置文件里出现时的限制条件与回调方法
- 实现上述回调方法,或使用Nginx预设的14个回调方法
- 合并不同级别的同名配置项
1、分配用于保存配置參数的数据结构
创建一个结构体,用来保存感兴趣的參数。比如定义一个以下的结构体:
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_str_t my_config_str; ngx_int_t my_config_num; } ngx_http_mytest_conf_t;
HTTP框架在解析配置文件nginx.conf时,仅仅要遇到http{}、server{}、location{}。就会分配一个保存參数的结构体。这些结构体由ngx_http_module_t接口中的回调方法生成:
typedef struct { ngx_int_t (*preconfiguration)(ngx_conf_t *cf); // 解析配置文件前调用 ngx_int_t (*postconfiguration)(ngx_conf_t *cf); // 解析完配置文件后调用 void *(*create_main_conf)(ngx_conf_t *cf); // 创建存储直属于http{}的配置项的结构体 char *(*init_main_conf)(ngx_conf_t *cf, void *conf); // 初始化main级别配置项 void *(*create_srv_conf)(ngx_conf_t *cf); // 创建存储直属于srv{}的配置项的结构体 char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf); // 合并main级别和srv级别的同名配置项 void *(*create_loc_conf)(ngx_conf_t *cf); // 创建存储直属于loc{}的配置项的结构体 char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf); // 合并srv级别和loc级别的同名配置项 } ngx_http_module_t;
详细步骤例如以下:
- 发现http{},调用create_main_conf、create_srv_conf、create_loc_conf
- 发现server{},调用create_srv_conf、create_loc_conf
- 发现location{}。调用create_loc_conf
普通HTTP模块一般仅仅实现create_loc_conf方法。用以匹配某个URL请求,定义方式例如以下:
static void* ngx_http_mytest_create_loc_conf(ngx_conf_t *cf) { 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->my_flag = NGX_CONF_UNSET; mycf->my_num = NGX_CONF_UNSET; mycf->my_str_array = NGX_CONF_UNSET_PTR; mycf->my_keyval = NULL; mycf->my_off = NGX_CONF_UNSET; mycf->my_msec = NGX_CONF_UNSET_MSEC; mycf->my_sec = NGX_CONF_UNSET; mycf->my_size = NGX_CONF_UNSET_SIZE; return mycf; }
2、设定配置项的解析方式
配置项的解析方式由ngx_command_t数组定义:
struct ngx_command_s { ngx_str_t name; // 配置项名称 ngx_uint_t type; // 配置项类型,包含该配置项能够出现的位置和能够携带參数的个数 // 出现name配置项后,调用此方法解析配置项參数 char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ngx_uint_t conf; // 配置文件里的偏移量,确定将该配置项放入哪个存储结构体中 ngx_uint_t offset; // 将该配置项放在存储结构体的哪个字段 void *post; // 配置项读取后的处理方法 };
成员含义例如以下:
- name:配置项名称
- type:决定配置项能够在哪些块中出现
- set:解析配置项的回调方法
- conf:告诉HTTP框架该配置项參数放在哪个结构体中。取值例如以下:
-
- NGX_HTTP_MAIN_CONF_OFFSET:用create_main_conf产生的结构体存储配置项參数
- NGX_HTTP_SRV_CONF_OFFSET:用create_srv_conf产生的结构体存储配置项參数
- NGX_HTTP_LOC_CONF_OFFSET:用create_loc_conf产生的结构体存储配置项參数
- 普通情况下。HTTP模块实现create_loc_conf回调方法。然后把conf设置为NGX_HTTP_LOC_CONF_OFFSET
- offset:表示当前配置项在整个存储配置项的结构体中的偏移位置。由宏offsetof(struct, member)计算偏移量。
Nginx的预设解析方法须要通过conf成员确定存储配置项參数结构体。然后通过offset成员确定存储配置项參数的成员。但自己定义解析方法把这一项设为0就可以,由于怎样存储是由我们定义的。
- post:多用途指针。一般不使用
3、解析配置项的14种预设方法
以ngx_conf_set_flag_slot为例,它用于解析带有on或off參数的配置项,相应的ngx_command_s结构例如以下:
{ ngx_string("test_flag"), NGX_HTTP_LOC_CONF | NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_mytest_conf_t, my_flag), NULL },
上述成员中。NGX_CONF_FLAG表示配置项仅仅有一个參数,且仅仅能是on或off。offsetof(ngx_http_mytest_conf_t, my_flag)表示将配置项test_flag后的參数存放在ngx_http_mytest_conf_t结构体的my_flag成员中。
其余13个预设方法类似。
4、自己定义配置项处理方法
该方法的目的就是把感兴趣的配置项參数存入预先分配好的结构体中:
static char* ngx_conf_set_myconfig(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { /* 參数conf就是http框架传给我们的。在ngx_http_mytest_create_loc_conf * 回调方法中分配的结构体ngx_http_mytest_conf_t */ ngx_http_mytest_conf_t *mycf = conf; /* cf->args是1个ngx_array_t队列。它的成员都是ngx_str_t结构。 * 我们用value指向ngx_array_t的elts内容,当中value[1]就是第1 * 个參数,同理value[2]是第2个參数 */ ngx_str_t* value = cf->args->elts; //ngx_array_t的nelts表示參数的个数 if (cf->args->nelts > 1) mycf->my_config_str = value[1]; if (cf->args->nelts > 2) { mycf->my_config_num = ngx_atoi(value[2].data, value[2].len); /* 字符串转整型 */ //假设字符串转化整型失败,将报"invalid number"错误,nginx启动失败 if (mycf->my_config_num == NGX_ERROR) return "invalid number"; } return NGX_CONF_OK; //返回成功 }
当存在例如以下ngx_command_s时:
{ ngx_string("test_myconfig"), NGX_HTTP_LOC_CONF | NGX_CONF_TAKE12, ngx_conf_set_myconfig, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL },
当在配置文件里出现test_myconfig配置项时,会调用我们自己定义的ngx_conf_set_myconfig方法解析配置项,存储配置项參数。比如:
test_myconfig test 123
那么解析完之后:
mycf->my_config_str = test
mycf->my_config_num = 123
5、配置项合并
若ngx_http_module_t.merge_loc_conf为NULL,那么外层配置块http{]和server{}中同名配置项都不会生效。所以,假设须要合并同名配置项,则定义merge_loc_conf函数。merge_srv_conf函数原理同样。怎样合并是自由的,比如merge_loc_conf能够例如以下定义:
static char * ngx_http_mytest_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { /* parent指向父配置块相应结构体。child指向子配置块相应结构体 */ ngx_http_mytest_conf_t *prev = (ngx_http_mytest_conf_t *)parent; ngx_http_mytest_conf_t *conf = (ngx_http_mytest_conf_t *)child; /* conf = prev或"defaultstr" */ ngx_conf_merge_str_value(conf->my_str, prev->my_str, "defaultstr"); /* conf = prev或0 */ ngx_conf_merge_value(conf->my_flag, prev->my_flag, 0); return NGX_CONF_OK; }
有10种合并配置项的方法,ngx_conf_merge_str_value和ngx_conf_merge_value是当中的两个。
參考:
《深入了解Nginx》 P115-P140.
版权声明:本文博客原创文章,博客,未经同意,不得转载。