聊一聊 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 转存到 variablesvariables_hash 中。

不难知道变量拥有两种存放方式,第一种是储存在一个全局的 hash 表里(variables_hash);第二种则是储存在一个全局动态数组里(variables),每个变量存在一个对应的索引。

2)变量信息结构体

存储变量属性、变量 set_handlerget_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 中。

posted @ 2020-11-30 07:26  Yeader  阅读(733)  评论(0编辑  收藏  举报