摘要:本文主要介绍了nginxhttp模块的数据存储结构概要,并通过示例代码进行了详细介绍,对大家的学习或工作有一定的参考价值。有需要的朋友下面和边肖一起学习。
从这一节开始,我们将开始解释http模块的实现原理。关于http模块很重要的一点是它如何存储http块、服务器块和位置块的数据,nginx的一些配置项可以在多个配置块中使用。当httpblock,serverblock,locationblock两个或两个以上的配置块都配置了这个配置项时,就会出现nginx如何处理这些配置项的问题。本文主要讲解如何在http块中存储各个模块的数据,这将是理解nginx的http模块工作模式的重要基石。
1。核心模块的存储模式
在nginx的运行过程中,有一个全局配置结构ngx_cycle_t,它有一个属性conf_ctx,是一个数组,存储了nginx的所有模块配置。这个数组的长度与nginx模块的数量相同。但是需要注意的是,conf_ctx数组的第一维度只存储核心模块的配置,而其他模块对应位置的数组元素实际上是空的。在conf_ctx中,每个核心模块配置结构的存储位置与该模块在所有模块(包括非核心模块)中的相对位置一致。下图是nginx存储核心模块的结构图:
此处标记的事件和http只是为了方便显示而添加的。本质上这个数组的元素类型就是void*的指针,这个指针指向的具体结构的类型是基于每个核心模块本身的定义。
在http模块下,指向一个ngx_http_conf_ctx_t类型的结构,用来存放http配置块中各个配置项的数据。这种结构的定义如下:
typedefstruct{ //存储MAIN级别配置 void**main_conf; //存储SRV级别配置 void**srv_conf; //存储LOC级别配置 void**loc_conf; }ngx_http_conf_ctx_t;我们知道,在nginx.conf配置文件中,http块下有一个服务器块,服务器块下也可以有一个位置块。更何况位置块下面还可以有子位置块,以此类推。这里ngx_http_conf_ctx_t结构的作用是存储这些配置对应的所有结构数据。首先我们需要明确的是,在nginx.conf配置文件中,配置项是由模块定义的,一个模块可以定义多个配置项。这些配置项的分析由本模块定义的方法完成。但是一般一个模块只定义一个结构,这个结构中的每个属性对应模块定义的每个配置项的数据。也就是说,通过每个模块定义的方法,其定义的配置项对应的配置会被转换成模块定义的结构。这里提到的结构对应于上面main_conf、srv_conf和loc_conf中的配置。从上面的定义可以看出,这三个属性的类型都是指针类型的数组,数组的长度对应着模块的数量,准确的说是对应着每个http模块。在解析每个http模块的配置之前,nginx会标记每个http模块在当前类型的模块(httpmodule)中的相对位置,每个http模块的相对位置对应上述三个属性的数组下标。如前所述,每个http模块只有一个配置结构来存储该模块定义的所有配置数据,这些配置结构存储在上述三个数组中。这样我们就可以理解,其实上面结构的三个属性,每个属性数组对应一个http模块的配置结构。
既然这里每个模块都有一个结构存储在数组对应的索引位置,为什么这里需要三个数组呢?比如对于ngx_http_core_module,它的相对位置是httpmodule中的第一位,也就是说main_conf[0]、srv_conf[0]和loc_conf[0]都存储了ngx_http_core_module的配置结构。为什么需要三个结构?这里需要说明的是,对于每个http模块,配置项会根据适用范围分为三类:仅针对http块、http块和服务器块、http块、服务器块和位置块。每种类型的配置项使用不同的结构。例如,ngx_http_core_module定义ngx_http_core_main_conf_t只存储http块的配置项,ngx_http_core_srv_conf_t存储http块和服务器块的配置项。Ngx_http_core_loc_conf_t定义为存储http块、服务器块和位置块的配置项。对应上面的数组,main_conf[0]的结构类型是ngx_http_core_main_conf_t,srv_conf[0]的结构类型是ngx_http_core_srv_conf_t,loc_conf[0]对应的结构类型是NGX_http_core_loc_这里我们要澄清一个问题。例如,如果在http块中配置了一个配置项,但其类型可以在http块、服务器块和位置块中使用,则它将存储在loc_conf[0]中。也就是说,上面的整个结构存储了httpblock中解析的所有配置项的数据。那么nginx如何标记一个CI,是这三种类型中的哪一种呢?这主要由ngx_command_t结构定义,如以下三种典型配置所示:
{ ngx_string("variables_hash_max_size"), NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof(ngx_http_core_main_conf_t,variables_hash_max_size), NULL }, { ngx_string("listen"), NGX_HTTP_SRV_CONF|NGX_CONF_1MORE, ngx_http_core_listen, NGX_HTTP_SRV_CONF_OFFSET, 0, NULL }, { ngx_string("root"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, ngx_http_core_root, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL },这里我们以variables_hash_max_size、listen和root为例。这三条指令都是ngx_http_core_module模块定义的配置项,但是存储位置完全不同。我们需要注意的是每条指令的第四个属性的定义:NGX_HTTP_MAIN_CONF_OFFSET,NGX_HTTP_SRV_CONF_OFFSET,NGX_HTTP_LOC_CONF_OFFSET。这三种类型的定义有两个含义,一个是表示这个配置项是只用于http块,还是可以用于http块和服务器块,或者可以用于http块、服务器块和位置块;另一层意思是在上面提到的ngx_http_conf_ctx_t中定义这个配置项的偏移量。所谓偏移量,就是在ngx_http_conf_ctx_t结构对象的指针地址已知的情况下,可以通过这里的偏移量计算出当前配置项存储的数组。这里我们需要展示一段代码,就是在ngx_conf_parse()方法中,主要用来解析nginx.conf的配置文件,一个配置项解析完之后,所有模块中都会找到该配置项的定义。如果找到一个配置项,它将尝试获取存储该配置项的结构,并调用该配置项指定的方法来解析配置项数据。当试图获取这里配置项对应的结构时,需要使用上面的偏移量。以下是获取CI的方法:
//查找配置对象,NGX_DIRECT_CONF常量单纯用来指定配置存储区的寻址方法,只用于core模块 if(cmd->type&NGX_DIRECT_CONF){ conf=((void**)cf->ctx)[cf->cycle->modules[i]->index]; //NGX_MAIN_CONF常量有两重含义,其一是指定指令的使用上下文是main(其实还是指core模块), //其二是指定配置存储区的寻址方法。 }elseif(cmd->type&NGX_MAIN_CONF){ conf=&(((void**)cf->ctx)[cf->cycle->modules[i]->index]); //除开core模块,其他类型的模块都会使用第三种配置寻址方式,也就是根据cmd->conf的值 //从cf->ctx中取出对应的配置。举http模块为例,cf->conf的可选值是NGX_HTTP_MAIN_CONF_OFFSET、 //NGX_HTTP_SRV_CONF_OFFSET、NGX_HTTP_LOC_CONF_OFFSET, //分别对应“http{}”、“server{}”、“location{}”这三个http配置级别。 //这个if判断的作用主要是,cf->ctx的类型是ngx_http_conf_ctx_t,而cmd->conf主要的值可选 //NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET、NGX_HTTP_LOC_CONF_OFFSET, //可以看到ngx_http_conf_ctx_t的属性有main_conf、srv_conf和loc_conf, //其实这里就是在计算当前的配置对象是存储在这三个数组中的哪一个数组中,以default_type指令为例, //其ngx_command_t的配置为: //{ngx_string("default_type"), //NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, //ngx_conf_set_str_slot, //NGX_HTTP_LOC_CONF_OFFSET, //offsetof(ngx_http_core_loc_conf_t,default_type), //NULL}, //可以看到,其conf属性的值为NGX_HTTP_LOC_CONF_OFFSET,则说明其是存储在loc_conf数组中的, //而该数组中的元素类型为ngx_http_core_loc_conf_t,因而可以看到,后面ngx_command_t //中offset属性的值就指定为了offsetof(ngx_http_core_loc_conf_t,default_type), //这就是在计算default_type属性在ngx_http_core_loc_conf_t结构体中的位置。 //通过下面的if判断第一步confp=*(void**)((char*)cf->ctx+cmd->conf);,就可以 //计算出当前所使用的结构体是在main_conf、srv_conf //和loc_conf的哪一个数组中,而通过第二步conf=confp[cf->cycle->modules[i]->ctx_index]; //的计算,就可以计算出该结构体在数组中的具体位置,并且获取该结构体数据。 //需要注意的是,这种计算方式只适用于http模块的配置项获取,因为只有http模块的配置结构体是 //ngx_http_conf_ctx_t类型的 }elseif(cf->ctx){ confp=*(void**)((char*)cf->ctx+cmd->conf); if(confp){ conf=confp[cf->cycle->modules[i]->ctx_index]; } }这里需要重点介绍最后一个elseif分支,它展示了http模块如何根据配置项的定义计算出配置项对应的结构的存储位置。下图显示了http块配置的整体结构:
2。服务器块的存储模式
如上所述,所有http块中的配置项都可以使用ngx_http_conf_ctx_t结构存储,那么服务器块中的配置项是如何存储的呢?主要存放在ngx_http_core_module模块的main_conf中,也就是上面main_conf[0]对应的ngx_http_core_main_conf_t结构中。这个结构有一个属性servers,类型是ngx_array_t,也就是一个数组。也就是说,在每个http配置块下,每个服务器配置块对应一个servers数组的元素,该数组的元素类型与http块的元素类型相同,即NGX_http_conf_CTX_T,但不同的是,当前配置项必须在服务器块或位置块中可用,而不仅仅是在http块中可用,因此配置项的类型必须是上面提到的NGX_HTTP_SRV_CONF_OFFSET和NGX_HTTP_LOC_CONF_OFFSET中的一个,而不是NGX_OFFSET所以虽然每个服务器配置块对应的配置结构是ngx_http_conf_ctx_t,但是它的main_conf数组不会有对应的配置项,只能从http块继承配置项。由于是继承,所以nginx的处理方式是将这个数组的指针直接指向http块对应的NGX_http_CONF_CTX_T的main_conf数组。以下是两种服务器块配置的示意图:
这个图看起来有点复杂,其实并不复杂。根据配置块,http块的配置存储在上面的ngx_http_conf_ctx_t中,而两个服务器块的配置存储在下面的两个ngx_http_conf_ctx_t中。中间的引用过程是通过http块的NGX_http_Core_Module模块。需要注意的是,在上面服务器块的配置中,main_conf指针是所指向的http块对应的NGX_http_CONF_CTX_T的main_conf属性。
3。定位块的存储方式
至于位置块的存储,它的存储结构仍然是ngx_http_conf_ctx_t,而且因为当前的配置项在位置块中,所以它的类型一定不能是NGX_HTTP_MAIN_CONF_OFFSET和NGX_HTTP_SRV_CONF_OFFSET,也就是说解析位置配置项得到的数据必须存储在loc_conf数组中。所以和服务器块一样,位置块对应的ngx_http_conf_ctx_t结构中的main_conf和srv_conf指向当前位置所在http块的main_conf和所在服务器块的srv_conf数组。
此外,在一个服务器块下将有多个位置块。在存储结构上,这些位置块被组织成一个队列。类似于服务器块,这个队列存储在所在服务器块对应的ngx_http_conf_ctx_t的loc_conf[0]中。这里的loc_conf[0]具有ngx_http_core_loc_conf_s的结构类型,并且它具有ngx_queue_t的属性,这是位置队列。最后,应该注意,这里的locations属性不仅仅表示服务器块下的多个位置块,因为可以在位置配置块下连续配置多个位置块,依此类推。这些子位置块的类型实际上是ngx_http_core_loc_conf_s,所以它们也可以用locations属性来表征。以下是添加到位置配置块的结构示意图:
该图显示两个位置并排排列。它们的main_conf和srv_conf分别指向http块的main_conf和当前位置块所在的服务器块的srv_conf,两个位置块对应的结构被组织在一个队列中的ngx_http_core_loc_conf_t中。
4。摘要
从ngx_cycle_t结构入手,介绍了http块的配置项在ngx_cycle_t中的存储方式,依次介绍了http块、服务器块、位置块的存储方式,以及相互之间的组织。
这就是本文的全部内容。希望对大家的学习有帮助,支持我们。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)