上一篇主要描述的是变量的使用,所以没涉及任何代码,而这一篇主要描述变量的实现原理,避免不了会涉及到一些底层代码,对于不了解c语言的同学读起来可能会有点吃力,这部分同学可以尝试一下两篇结合着读,比如先读一个知识点的用法,然后再回到这篇来看一下实现原理,以此来加深理解。如果在读的过程中你有发现任何问题,还请反馈给我,我会非常感激。
1.ngx内部如何表示变量
nginx内部用了两种结构体来表示变量,一个用来表示变量的名字,另一个用来表示变量的值,分别是:
src/http/ngx_http_variables.h/ngx_http_variable_t; src/http/ngx_http_variables.h/ngx_http_variable_value_t;
nginx内部在定义变量的时候,实际上就是在创建ngx_http_variable_t结构体。而代表变量值的结构体ngx_http_variable_value_t,则是通过绑定在ngx_http_variable_t结构体上的get_handler()方法创建或获取的。比如set指令:
set $a “I am var”;
该指令用来定义一个变量“$a”,并且赋值为“I am var”。
按照上面的描述,当解析到这个set指令的时候,在nginx内部会创建一个ngx_http_variable_t结构体表示变量名“a”,而当需要获取这个变量值的时候,会调用这个变量上绑定的get_handler()方法,该方法会创建或者从缓存中获取一个ngx_http_variable_value_t结构体,对应的变量值“I am var”就存在于这个值结构体中。
把这两个结构体的代码贴过来看看它更详细的表示。
表示变量名的结构体有如下字段:
typedef struct ngx_http_variable_s ngx_http_variable_t; struct ngx_http_variable_s { ngx_str_t name; ngx_http_set_variable_pt set_handler; ngx_http_get_variable_pt get_handler; uintptr_t data; ngx_uint_t flags; ngx_uint_t index; };
表示变量值的结构体有如下字段:
typedef ngx_variable_value_t ngx_http_variable_value_t; typedef struct { unsigned len:28; unsigned valid:1; unsigned no_cacheable:1; unsigned not_found:1; unsigned escape:1; u_char *data; } ngx_variable_value_t;
其中ngx_http_variable_s#name字段用来存放变量的名字“a”,而变量值则用到了ngx_http_variable_value_t结构体中的两个字段len和data,data用来指向变量值字符串的首地址,len则表示字符的长度。
1.1 nginx中的变量值都是字符型的吗?
从上面两个结构体中可以看到,用来存放变量值的字段是一个无符号char类型的指针(c中一般用它来表示字符串),该类型的指针类似于java语言中的String类型,例如:
u_char *data = “hahaha”;
String data = “hahaha”:
这两句的作用都是定义并初始化一个字符串,表面上看我们会认为nginx中的变量“应该”只有字符变量这一种类型,但是上一篇我们也提到了一个非字符型变量“${binary_remote_addr}”的例子,能够出现这种情况其实来自于C语言的灵活性。下面通过一个片段代码来说明它是怎么做到的:
src/http/ngx_http_variables.c/ngx_http_variable_binary_remote_addr() v->len = sizeof(in_addr_t); v->data = (u_char *) &sin->sin_addr;
其中v是ngx_http_variable_value_t对象,可以看到nginx把代表网络地址(sin->sin_addr)的这个变量的本身的地址转换成了一个u_char类型的指针,并赋值给v->data字段,这种转换在c语言中是合法的,然后v->len字段表示了这个网络地址所占的字节个数(sin