现在的位置: 首页 > 综合 > 正文

【Nginx】初识nginx—配置文件解析

2014年01月31日 ⁄ 综合 ⁄ 共 11488字 ⁄ 字号 评论关闭

其实实现Nginx的每一个模块都是在实现ngx_module_t结构体,当你实现了这个结构你的模块也就实现了!

Nginx强大的功能,在一定程度上是依赖于对配置文件的配置,因此要正确的解析用户的配置文件就显得格外重要。从Nginx中解析出的所有配置信息都是由ngx_cycle_t结构体中的conf_ctx存储的,它是这样定义的

struct ngx_cycle_s{
      .......
      void ****conf_ctx;
      ......

}

她是一个指针数组,其中每一个元素又指向一个指针数组。存储结构类似于下图:

这样的指针指来指去,显得有点乱,但是Nginx正是利用了指针的强大功能,把所有的配置信息有条不紊的组织了起来。至于内部如何组织的我也没太高明白,这边博文住一套是开发一个模块来演示,自己定义的配置项如何在Ngins中解析出来,共模块使用。

1,、定义自己配置项的结构体。

//存储配置项参数的结构体  
//由于Nginx框架提供了预设的14个回调方法,用于解析配置文件,所以在这里定义15个成员用于实现14种解析方法的实现和自定义解析方法
typedef struct{  
         ngx_str_t r_str; 
         ngx_int_t r_num;  
         ngx_flag_t r_flag;  
         size_t r_size;  
         ngx_array_t* r_str_array;  
         ngx_array_t* r_keyval;  
         off_t r_off;  
         ngx_msec_t r_msec;  
         time_t r_sec;  
         ngx_bufs_t r_bufs;  
         ngx_uint_t r_enum_seq;  
         ngx_uint_t r_bitmask;  
         ngx_uint_t r_access;  
         ngx_path_t* r_path;  
	 ngx_str_t r_myprase;
}ngx_http_mytest4_loc_conf_t; 

2,定义自己的creat_loc_conf方法,用于产生自己配置结构体的指针,供Nginx框架使用

//Nginx定义了三个级别的配置main、srv、loc,分别表示直接出现在http{}、server{}、location{}块内的配置项,当配置文件中出现http{}时,Nginx会接管http块的解析,http框架会调用所有可能实现的create_main_conf,create_srv_conf,create_loc_conf方法生成存储main级别的配置项的结构体,当遇到server{}块时,http框架会调用所有可能实现的create_srv_conf,create_loc_conf方法生成存储srv级别的配置项的结构体,遇到location{}块时,http框架会调用所有可能实现的create_loc_conf方法生成存储loc级别的配置项的结构体,
//普通的http模块一般只实现craete_loc_conf方法,因为他只关心匹配特定的URL请求。

//实现自己的create_loc_conf方法,并初始化部分成员(初始化是Nginx框架内置解析函数所要求的)
static void * ngx_http_mytest4_craete_loc_conf(ngx_conf_t *cf){
	//此方法就是用来生成自定义结构体的地址
	ngx_http_mytest4_loc_conf_t *conf;  
	printf("create_loc_conf!!\n");
	conf = ngx_pcalloc(cf->pool,sizeof(ngx_http_mytest4_loc_conf_t));  
	if(NULL == conf){  
		return NGX_CONF_ERROR;  
	 }  


	//如果使用内置的解析函数,则必须初始化
	conf->r_flag = NGX_CONF_UNSET;  
	conf->r_num = NGX_CONF_UNSET;  
	conf->r_str_array = NGX_CONF_UNSET_PTR;  
	conf->r_keyval = NULL;  
	conf->r_off = NGX_CONF_UNSET;  
	conf->r_msec = NGX_CONF_UNSET_MSEC;  
	conf->r_sec = NGX_CONF_UNSET;  
	conf->r_size = NGX_CONF_UNSET_SIZE;  
	return conf;  
}

3、定义配置项解析相关的结构体

//定义模块配置文件参数 ,指定配置项的解析函数 
static ngx_command_t ngx_http_mytest4_commands[] = {  
     {  //解析ngx_str_t类型的配置项,配置项的名称是test_str,其后只能有一个参数,将它保存在ngx_http_mytest_loc_conf_t结构的r_str成员中
   ngx_string("test_str"), 
	//配置项类型,即定义他可以出现的位置 
   NGX_HTTP_MAIN_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,  
        //处理配置项参数的函数,使用内置的ngx_conf_set_str_solt,
	ngx_conf_set_str_slot,  
	//指示配置项所处内存的相对位置,因为http模块可能会定义多个结构体,此成员说明用那个级别的结构体存储
   NGX_HTTP_LOC_CONF_OFFSET,
	//指示当前配置文件在配置项的结构体中的的偏移位置,这种工作不需要用户来完成,可以使用offsetof宏来实现
	//define offsetof(type,member) (size_t)&(((type*)0)->member)  
   offsetof(ngx_http_mytest4_loc_conf_t,r_str),  
	//配置项读取后的处理方法
   NULL  
     },  

{  //配置项的参数只有一个,且只能是数字,配置项的名称是test_num,将他的参数保存在ngx_http_mytest_loc_conf_t结构的r_num成员中
   ngx_string("test_num"), 
	NGX_HTTP_MAIN_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 
   //NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,  
	ngx_conf_set_num_slot,  
	NGX_HTTP_LOC_CONF_OFFSET,
	offsetof(ngx_http_mytest4_loc_conf_t,r_num),  
	NULL  
     },  

{  //配置项的名称是test_flag,其后的参数必须是on或者off,将他的参数保存在ngx_http_mytest_loc_conf_t结构的r_flag成员中,此成员必须在create_loc_conf方法中初始化
	ngx_string("test_flag"), 
	NGX_HTTP_MAIN_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_FLAG, 
	//NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,  
	ngx_conf_set_flag_slot,  
	NGX_HTTP_LOC_CONF_OFFSET,
	offsetof(ngx_http_mytest4_loc_conf_t,r_flag),  
	NULL  
     },  
{  //配置项的名称是test_size,想表达的含义是空间大小,参数后可以有单位,如m,M,k,K(1k=1024),不允许出现g和G,解析后的单位是字节,解析后报存在gx_http_mytest_loc_conf_t结构的r_size成员中,此成员必须在create_loc_conf方法中初始化
   ngx_string("test_size"), 
	NGX_HTTP_MAIN_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 
   //NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,  
	ngx_conf_set_size_slot,  
   NGX_HTTP_LOC_CONF_OFFSET,
   offsetof(ngx_http_mytest4_loc_conf_t,r_size),  
   NULL  
     },  
{  //配置项名称是test_str_array,希望出现多个同名配置项,配个配置项后跟一个字符串参数,使用ngx_conf_set_str_array_solt方法可以把所有参数都以ngx_str_t类型存放到ngx_array_t的队列中,解析后保存在gx_http_mytest_loc_conf_t结构的r_str_array成员中
	ngx_string("test_str_array"),
	NGX_HTTP_MAIN_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 
	//NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,  
	ngx_conf_set_str_array_slot,  
	NGX_HTTP_LOC_CONF_OFFSET,
	offsetof(ngx_http_mytest4_loc_conf_t,r_str_array),  
	NULL  
     },  
{  //与上面的ngx_conf_set_str_array_solt解析方法类似,只是他要求后跟两个参数,表示key/value,如果ngx_array_t*类型的r_keyval存储,则必须设置NGX_CONF_TAKE2,表示后跟两个参数
	ngx_string("test_keyval"), 
	NGX_HTTP_MAIN_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE2, 
	//NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE2,  
	ngx_conf_set_keyval_slot,  
	NGX_HTTP_LOC_CONF_OFFSET,
	offsetof(ngx_http_mytest4_loc_conf_t,r_keyval),  
	NULL  
     },  
{  //与ngx_conf_set_size_solt方法类似,只不过ngx_conf_set_off_solt方法允许单位使用g和G,解析后的偏移量是字节单位的,
	ngx_string("test_off"), 
	NGX_HTTP_MAIN_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 
	//NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,  
	ngx_conf_set_off_slot,  
	NGX_HTTP_LOC_CONF_OFFSET,
	offsetof(ngx_http_mytest4_loc_conf_t,r_off),  
	NULL  
     },  
{  //配置项名为test_msec,要表达的是时间的长短,可以使用s(秒,如果不使用单位则默认为秒),m(分钟),h(小时),d(天),w(周),M(月),y(年),解析后的单位是毫秒,
	ngx_string("test_msec"), 
	NGX_HTTP_MAIN_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 
	//NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,  
	ngx_conf_set_msec_slot,  
	NGX_HTTP_LOC_CONF_OFFSET,
	offsetof(ngx_http_mytest4_loc_conf_t,r_msec),  
	NULL  
     },  
{  //与上面类似,只不过解析后的单位是秒
	ngx_string("test_sec"), 
	NGX_HTTP_MAIN_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 
	//NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,  
	ngx_conf_set_sec_slot,  
	NGX_HTTP_LOC_CONF_OFFSET,
	offsetof(ngx_http_mytest4_loc_conf_t,r_sec),  
   NULL  
     },  
{  //ngx_conf_set_bufs_solt方法要求配置项后必须跟两个参数,通常第一个参数表示缓冲区的个数,第二个参数表示单个缓冲区的空间大小(使用的单位和ngx_conf_set_size_solt的单位一致),解析后大小以字节为单位
	ngx_string("test_bufs"), 
	NGX_HTTP_MAIN_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE2, 
	//NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,  
	ngx_conf_set_bufs_slot,  
	NGX_HTTP_LOC_CONF_OFFSET,
	offsetof(ngx_http_mytest4_loc_conf_t,r_bufs),  
	NULL  
     },  
{  //test_enum_seq用来表示枚举配置项,也就是他的参数只能从给定的范围中取,解析后的参数为uint类型,注意:为了实现枚举,需要自己定义枚举类型,并通过psot指针传入,即第六个参数
	ngx_string("test_enum"), 
	NGX_HTTP_MAIN_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 
	//NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,  
	ngx_conf_set_enum_slot,  
	NGX_HTTP_LOC_CONF_OFFSET,
	offsetof(ngx_http_mytest4_loc_conf_t,r_enum_seq),  
	test4_enums  //传入枚举类型  
     },  
{  //与上面类似,配置项也必须是枚举类型,差别在效率方面,ngx_conf_set_bitmask_solt可以按照位比较,效率更高,
	ngx_string("test_bitmask"), 
	NGX_HTTP_MAIN_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 
	//NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,  
	ngx_conf_set_bitmask_slot,  
	NGX_HTTP_LOC_CONF_OFFSET,
	offsetof(ngx_http_mytest4_loc_conf_t,r_bitmask),  
	test4_bitmasks//传入枚举类型  
     },  
{  //ngx_conf_set_access_solt用来解析读取权限,配置项后可以跟1到3个参数,解析后是一个uint类型(权限的掩码)
	ngx_string("test_access"), 
	NGX_HTTP_MAIN_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE123, 
	//NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,  
	ngx_conf_set_access_slot,  
	NGX_HTTP_LOC_CONF_OFFSET,
	offsetof(ngx_http_mytest4_loc_conf_t,r_access),  
	NULL  
     },  
{  //ngx_conf_set_path_solt用来解析路径,后可以跟1-4个参数,第一个参数必须是路径,其余参数必须是整数(大部分情况下可以不使用),会将参数解析后保存在ngx_path_t类型的结构中,
	ngx_string("test_path"), 
	NGX_HTTP_MAIN_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1234, 
	//NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,  
	ngx_conf_set_path_slot,  
	NGX_HTTP_LOC_CONF_OFFSET,
	offsetof(ngx_http_mytest4_loc_conf_t,r_path),  
	NULL  
     },  
  {  //自定义解析函数
	ngx_string("test_myparse"), 
	//配置项类型,即定义他可以出现的位置 
	NGX_HTTP_MAIN_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, 
	//NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,  
        //处理配置项参数的函数,函数在下面定义 
	ngx_conf_set_myparse,  
	//在配置文件中的偏移量
	NGX_HTTP_LOC_CONF_OFFSET,
	//预设的解析方法配置项  
         0,  
	//配置项读取后的处理方法
         NULL  
     },  
	//command数组要以ngx_null_command结束
	//#define ngx_null_command {ngx_null_string,0,NULL,0,0,NULL}
	ngx_null_command  
 };  

上面指定了15个配置项参数的解析函数,其中前14个都是使用框架提供的解析方法,因此部分配置项,需要传递自定义的结构体参数供框架使用,自定义的两个结构体如下:

//用于测试ngx_conf_set_enum_slot方法,他的值必须是枚举中的一个
static ngx_conf_enum_t test4_enums[]={
{ngx_string("apple"),1},
{ngx_string("banana"),2},
{ngx_string("orange"),3},
{ngx_null_string,0}
};

//用于测试ngx_conf_set_bitmask_slot方法
static ngx_conf_bitmask_t  test4_bitmasks[]={
{ngx_string("good"),0x0002},
{ngx_string("better"),0x0004},
{ngx_string("best"),0x0008},
{ngx_null_string,0}
};

其中第15个配置项使用了自己的解析函数,同时在这个方法中指定了匹配这个localtion时执行的回调方法,以展示我们解析出的配置项:

//自定义的配置项解析函数,当配置项中出现test_myparse配置项时将调用这个函数  
 static char *  ngx_conf_set_myparse(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)  
 { 
	printf("myparse!!\n");
	 //conf参数是http框架传递给用户在ngx_http_mytest_craete_loc_conf函数分配的结构体
	ngx_http_mytest4_loc_conf_t *mycf = conf; 

	//cf中的成员args是一个ngx_array_t类型的队列,他的成员都是ngx_str_t类型的字符串,用value指向ngx_array_t的elts成员,value[1]就是第一个参数,value[2]就是第二个参数,args中的nelts表示参数的个数

	ngx_str_t *value = cf->args->elts;

	if(cf->args->nelts>1){//第一个参数
		//直接赋值,ngx_str_t只是指针的传递
		mycf->r_myprase = value[1];
	}
   	
//-----------------------------------------------
	//ckcf并不是指特定的location块内的数据结构,他可以是mian、srv、loc级别的配置项
	//每个http{},sever{},location{}都有一个ngx_http_core_loc_conf_t类型的数据结构
     	ngx_http_core_loc_conf_t *clcf;  
   	
	//找到mytest配置项所在的配置块
    	clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);  
   	
	//http框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段是,如果请求的主机名,URI与配置项所在的配置块相匹配时,就调用
	//clcf中的handle方法处理这个请求
     	//NGX_HTTP_CONTENT_PHASE用于处理http请求内容的阶段,这是大部分http模块通常介入的阶段
	clcf->handler = ngx_http_mytest4_handler;  
//------------------------------------------------------------------

	return NGX_CONF_OK;  
 }  
    

4、定义模块上下文,我们只实现了creat_loc_conf方法:

 //mytest模块上下文,都为NULL即是说在http框架初始化时没有什么要做  
 static ngx_http_module_t ngx_http_mytest4_module_ctx = {  
     NULL,  
     NULL,  
     NULL,  
     NULL,  
     NULL,  
     NULL,  
     ngx_http_mytest4_craete_loc_conf,  //自定义的配置项结构体生成函数
     NULL  
 };  

5,定义自己的模块,在编译时加入到全局的ngx_modules数组中,这样在Nginx初始化时会调用模块的所有初始化方法

 ngx_module_t ngx_http_mytest4_module = {  
     NGX_MODULE_V1, //由Nginx定义的宏来初始化前七个成员 
     &ngx_http_mytest4_module_ctx,  //模块的上下文结构体,指向特定模块的公共方法
     ngx_http_mytest4_commands,  //处理配置项的结构体数组
     NGX_HTTP_MODULE,  //模块类型
    //Nginx在启动停止过程中七个执行点的函数指针
     NULL,  
     NULL,  
     NULL,  
     NULL,  
     NULL,  
     NULL,  
     NULL,  

     NGX_MODULE_V1_PADDING  //由Nginx定义的宏定义剩下的8个保留字段
 };  

6,实现自己展示的回调函数:

  //实际完成处理的回调函数  
  static ngx_int_t ngx_http_mytest4_handler(ngx_http_request_t *r)  
  {  
	printf("handle!!\n");
	//请求方法
	if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) {  
          return NGX_HTTP_NOT_ALLOWED;  
      }  
	//不处理请求的包体,直接丢弃。但这一步也是不可省略的,他是接受包体的一种方法,只不过是简单的丢弃,
	//如果不接受,客户端可能会再次试图发送包体,而服务器不接受就会造成客户端发送超时
	ngx_int_t rc = ngx_http_discard_request_body(r);  
	if (rc != NGX_OK) {  
   	return rc;  
      }  

	//存储配置项参数的结构体  
	ngx_http_mytest4_loc_conf_t *mycf;
	//取得模块上下文
	mycf = ngx_http_get_module_loc_conf(r,ngx_http_mytest4_module);  

	
	//构造响应头部
	ngx_str_t type = ngx_string("text/plain");  

	//以下是将内存中的字符串做为包体发送
	ngx_str_t response = ngx_string("conf:\ntest_str\t:\t%s;\ntest_num\t:\t%d;\ntest_size\t:\t%dbyte;\ntest_myparse\t:\t%s;\n");  
	ngx_str_t test_str = mycf->r_str; 
        ngx_int_t test_num = mycf->r_num;  
        size_t test_size = mycf->r_size;  
	ngx_str_t data = mycf->r_myprase;
	//8是共有四个占位符,每个占位符两个字节4*2=8
	printf("sizeof(test_num):%d\tsizeof(test_size):%d\n",sizeof(test_num),sizeof(test_size));
	int response_len = response.len+test_str.len+sizeof(test_num)+sizeof(test_size)+data.len-8+1;

	r->headers_out.status = NGX_HTTP_OK;  
	r->headers_out.content_length_n = response_len;  
	r->headers_out.content_type = type; 
 
	//发送http头部,其中也包括响应行
	rc = ngx_http_send_header(r);  
	if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {  
   	return rc;  
      }
	
   
	ngx_buf_t *b;  
     //根据请求中传来的内存池对象,创建内存buf
	b = ngx_create_temp_buf(r->pool, response_len);  
	if (b == NULL) {  
    	return NGX_HTTP_INTERNAL_SERVER_ERROR;  
      }  
	
	ngx_snprintf(b->pos,response_len,(char *)response.data,test_str.data,test_num,test_size,data.data); 

      //有效内容到last结束
	b->last = b->pos + response_len;  
      //因为ngx_buf_t可以由ngx_chain_t链表链起来,last_buf可以标记这是最后一块待处理的缓冲区,简化处理
	b->last_buf = 1;  
   //将内存buf用链表链起来,作为ngx_http_output_filter的跌入个参数
	ngx_chain_t out;  
	out.buf = b;  
    //标记这是最后一个ngx_chain_t
	out.next = NULL;  
	return ngx_http_output_filter(r, &out);  
  	
  }  

7、测试:

配置文件如下:(实现了个别的解析和输出,其余的是一样的!)

展示:

8、总结:

使用Nginx框架提供的配置项解析函数,可以大大提高开发效率,同时减少代码下出错的可能,但是注意,使用有些解析方法是,结构体中的相应项,一定要初始化,以防解析错误。

抱歉!评论已关闭.