这一部分我们将探讨一些较细节的东西,加上前面(一),(二)两篇文章对典型情景的分析,我相信应该会对大家理解nginx的脚本解析机制有很大的帮助的。
这里我们关注ngx_http_core_main_conf_t(下面简写为cmcf)结构中的这两个成员,cmcf->variables_keys和cmcf->variables,其中variables_keys是个hash数组,变量hash值相同的放到一个数组中,而且是唯一的,重复时会报错。重要的是这个hash数组,即cmcf->variables_keys,保存了整个系统中所有预定义的,自定义,几乎所有的变量(一些特定前缀的变量除外,如“http_”等,在函数ngx_http_variables_init_vars中都列出来了)。
我们分条目来看:
1. 系统中所有可能用到变量都会放到cmcf->variables_keys中。
2. 配置中出现的变量(在处理请求时会用到的),会放到cmcf->variables中。总体上来看,除去一些有特定前缀的变量(这类变量后面会讲),cmcf->variables可以看做是ngx_http_variables_init_vars的子集。从这里的设计可以看出来,系统定义的变量在请求处理时,并不会全部用到,我们只需根据配置,拿到有用的就可以了,所以cmcf->variables_keys使用了temp_pool(实际的元素则使用cf->pool),当variables收集变量完成之后,variables_keys结构也就没什么用处了。
3. cmcf->variables是一个数组,它的元素类型为ngx_http_variable_t。
struct ngx_http_variable_s {
// 变量名字符串
ngx_str_t name;
// 使用变量中的值设置request的某个成员的值,所谓set
ngx_http_set_variable_pt set_handler;
// 根据request中成员(如uri,args等)的值来设置,r->variables中对应变量的内容。
ngx_http_get_variable_pt get_handler;
// 需要具体处理的具体内容,因为这个结构是通用的,那么具体处理的信息就放到// data成员中
uintptr_t data;
// 一些标识信息,区别不同处理,后面会详细讲
ngx_uint_t flags;
// 该变量在cmcf->variables数组中的下标
ngx_uint_t index;
};
4. 在init_request阶段,我们看到有这样的处理:
r->variables =ngx_pcalloc(r->pool, cmcf->variables.nelts * sizeof(ngx_http_variable_value_t));
这里的设计思想是这样的,cmcf->variables是每个变量的处理信息的封装,而r-> variables里面则是处理该变量得到的内容(即value),所以cmcf->variables跟r->variables是一一对应的,成员之间就是var对value的关系,这不过这里的var和value是封装了很多信息的结构,不是单纯的值或字符串。这里我们看下r->variables的成员。
typedef struct {
unsigned len:28;
unsigned valid:1; // 表示该变量在数组中并且有效,可以使用
// 表示使用该变量时,需要重新通过get_handler来获取,因为该变量的实际内容// 可能已经改变了。
unsigned no_cacheable:1;
// 特定位置上的变量不存在(实际上是没有设置),那么它就是“not found”!
unsigned not_found:1;
unsigned escape:1; // 这里不管它,跟我们的情景没有多大关系
u_char *data;
} ngx_variable_value_t;
我们看到这个结构中处理包含代表直接变量值的指针和大小(即data和len)外,剩下的都是一些标记位。
下面我们看几个宏:
#define NGX_HTTP_VAR_CHANGEABLE 1
#define NGX_HTTP_VAR_NOCACHEABLE 2
#define NGX_HTTP_VAR_INDEXED 4
#define NGX_HTTP_VAR_NOHASH 8
1. NGX_HTTP_VAR_CHANGEABLE
使用这个宏的时候意味着,该变量可以重复配置,一般后配置的会覆盖前面的配置。如:
set $file $1
set $file $2
那么结果就是file的最终值就是$2所代表的。
2. NGX_HTTP_VAR_NOCACHEABLE
这个宏所影响的正是ngx_variable_value_t结构中的no_cacheable,前面注释里面提到过,凡是有该标记的变量,都要通过get_handler来获取变量的值。如下面函数:
ngx_http_get_flushed_variable(ngx_http_request_t *r, ngx_uint_tindex)
{
ngx_http_variable_value_t *v;
v = &r->variables[index];
// 找到变量,发现是有效的,但是由于属于NGX_HTTP_VAR_NOCACHEABLE,所以// 需要在cmcf->variables找到它的处理结构来重新获取新值。
if (v->valid) { // 变量有效,具备可以使用的潜质
if (!v->no_cacheable) { //可以直接用,就返回了
return v;
}
// 这是一个NGX_HTTP_VAR_NOCACHEABLE类型的变量,那么…
v->valid = 0;
v->not_found = 0;
}
// 重新获取,这个函数就不看了,大家可以自己分析
returnngx_http_get_indexed_variable(r, index);
}
这种变量往往是跟特定的请求紧密相关的,如host,uri,args之类的,处理时每次重新获取新值时必要的。
3. NGX_HTTP_VAR_INDEXED和NGX_HTTP_VAR_NOHASH
这两个宏主要是在SSI相关处理中用到,而ssi的处理需要用到cmcf->variables_hash,这是hash表,可以高效的找到变量。NGX_HTTP_VAR_NOHASH类型的变量压根就不会被放到这个hash表中,而从hash表中找到的变量,是ngx_http_variable_t结构,如果我们从它flags发现NGX_HTTP_VAR_INDEXED标记,那么意味着我们可以直接到r->variables中去找,至于找到后可用与否,那要另作判断了!