聊一聊 Nginx 变量(一)
一、什么是变量?
变量可以认为是存放“值”的容器。而所谓“值”,在许多编程语言里,既可以是 3.14
这样的数值,也可以是 hello world
这样的字符串,甚至可以是像数组、哈希表这样的复杂数据结构。
Nginx 的变量和 perl、php 等语言的类似,由美元符号 $ 开头,随后跟着一个字符串,代表这个变量的名称,例如 $name
,可选地,这个字符串可以用花括号包围,譬如 ${name}
,合法的变量名可用字符集为 [a-zA-Z0-9_]。特别地,Nginx 支持正则子组,即 $1,$2 这样的变量。变量值只有字符串这一种类型。
例子:
nginx.conf
文件中有以下配置:
server {
listen 8080;
location /test {
set $foo hello;
echo "foo: $foo";
}
}
使用 curl
这个 HTTP 客户端在命令行上请求这个 /test
接口,可以得到:
$ curl 'http://localhost:8080/test'
foo: hello
我们通过 set 配置指令对变量 $foo
进行了赋值操作, 把字符串 hello
赋给了它,通过“变量插值”的形式将其拼接到字符串中。
二、变量定义
1、主要数据结构
1)变量跟踪结构体
维护 Nginx 各模块支持的和配置文件中用到的变量信息结构体。
typedef struct {
...
ngx_hash_t variables_hash;
ngx_array_t variables; /* array of ngx_http_variable_t */
..
ngx_hash_keys_arrays_t *variables_keys; /* 解析时的临时存储 */
...
} ngx_http_core_main_conf_t;
variables_keys
只是在解析时使用的临时存储,配置解析完成后,其中的变量信息会被 ngx_http_variables_init_vars
转存到 variables
和variables_hash
中。
不难知道变量拥有两种存放方式,第一种是储存在一个全局的 hash 表里(variables_hash);第二种则是储存在一个全局动态数组里(variables),每个变量存在一个对应的索引。
2)变量信息结构体
存储变量属性、变量 set_handler
和 get_handler
和这两个函数用到的参数值。
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;
};
变量的属性是以下几种属性的单例或组合:
NGX_HTTP_VAR_CHANGEABLE
变量可被覆盖
NGX_HTTP_VAR_NOCACHEABLE
不可缓存,对于 noncacheable 的变量,每次取值时都需要调用其 get_handler
NGX_HTTP_VAR_INDEXED
保存在动态数组中
NGX_HTTP_VAR_NOHASH
不保存在 hash 表中
NGX_HTTP_VAR_PREFIX
前缀型变量,如 arg_
和 cookie_
示例:
static ngx_http_variable_t ngx_http_fastcgi_vars[] = {
{ ngx_string("fastcgi_script_name"), NULL,
ngx_http_fastcgi_script_name_variable, 0,
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
{ ngx_string("fastcgi_path_info"), NULL,
ngx_http_fastcgi_path_info_variable, 0,
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
ngx_http_null_variable
};
3)变量值结构体
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;
4)变量获取接口
/* 获取没有索引的变量 */
ngx_http_variable_value_t *ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key)
/* 根据索引获取变量,对于 nocaheable 变量会重新获取 */
ngx_http_variable_value_t *ngx_http_get_flushed_variable(ngx_http_request_t *r, ngx_uint_t index)
/* 根据索引获取变量 */
ngx_http_variable_value_t *ngx_http_get_indexed_variable(ngx_http_request_t *r, ngx_uint_t index);
2、变量分类
1)核心模块 ngx_http_core_module
提供的(内建)变量
使用 ngx_http_core_variables
描述, 由 preconfiguration
回调函数 ngx_http_variables_add_core_vars
进行定义:
ngx_int_t
ngx_http_variables_add_core_vars(ngx_conf_t *cf)
{
...
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
cmcf->variables_keys = ngx_pcalloc(cf->temp_pool,
sizeof(ngx_hash_keys_arrays_t));
...
cmcf->variables_keys->pool = cf->pool;
cmcf->variables_keys->temp_pool = cf->pool;
ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL)
for (v = ngx_http_core_variables; v->name.len; v++) {
rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v,
NGX_HASH_READONLY_KEY);
...
}
...
}
2)其他功能模块中添加的变量
如 ngx_http_fastcgi_module
提供的变量使用 ngx_http_fastcgi_vars
描述,由 preconfiguration
回调函数 ngx_http_fastcgi_add_variables
进行定义:
static ngx_int_t
ngx_http_fastcgi_add_variables(ngx_conf_t *cf)
{
ngx_http_variable_t *var, *v;
for (v = ngx_http_fastcgi_vars; v->name.len; v++) {
var = ngx_http_add_variable(cf, &v->name, v->flags);
...
var->get_handler = v->get_handler;
var->data = v->data;
}
return NGX_OK;
}
3)使用 set
创建的变量
由 ngx_http_rewrite_module
提供的 set
指令定义的自定义变量由其配置解析函数 ngx_http_rewrite_set
进行定义:
static char *
ngx_http_rewrite_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
...
value = cf->args->elts;
...
value[1].len--;
value[1].data++;
v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
...
index = ngx_http_get_variable_index(cf, &value[1]);
...
}
上述三种类型变量信息都被直接 (ngx_hash_add_key
) 或间接 (ngx_http_add_variable
) 存储到了 variables_keys
中。