在Nginx中给变量赋值

记录一下在Nginx中给变量赋值的方法;)

一般在开发模块的时候, 主要是去获取变量的值, 往往很少去主动设置变量的值. 即使有这种情况, 也通常是使用rewrite模块, map(模块)或者ngx_lua模块去设置. 不过最近需要根据不同的情况去动态的设置某一个变量, 因此小小的看了一下对应的方法.

先推荐一下讲解Nginx变量的相关资源:

  1. 首先推荐Agentzh大大的系列教程
  2. Tengine团队的有关Nginx变量文章

怎么搞

既然已经有多个模块可以达到相同的功能, 那最简单的办法就是直接看一下代码, 模仿着写.

最先看的是rewrite模块的代码, 但是发现对应的代码块稍微显得复杂了一点, set指令里面还包含了导出变量等操作. 于是准备看看ngx_lua, 与设置变量的相关的代码主要在ngx_http_lua_variable.c文件的ngx_http_lua_var_set函数中:

ngx_http_lua_variable.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
 * Set nginx internal variable content
 *
 * @retval Always return a boolean on Lua stack. Return true when variable
 * content was modified successfully, false otherwise.
 * @seealso ngx_http_lua_var_get
 * */
static int
ngx_http_lua_var_set(lua_State *L)
{
    ngx_http_variable_t         *v;
    ngx_http_variable_value_t   *vv;
    ngx_http_core_main_conf_t   *cmcf;
    u_char                      *p, *lowcase, *val;
    size_t                       len;
    ngx_str_t                    name;
    ngx_uint_t                   hash;
    ngx_http_request_t          *r;
    int                          value_type;
    const char                  *msg;

    r = ngx_http_lua_get_req(L);
    if (r == NULL) {
        return luaL_error(L, "no request object found");
    }

    ...

    /* we fetch the variable itself */

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    v = ngx_hash_find(&cmcf->variables_hash, hash, name.data, name.len);

    if (v) {
        if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {
            return luaL_error(L, "variable \"%s\" not changeable", lowcase);
        }

        if (v->set_handler) {

            ...

            } else {
                vv->valid = 1;
                vv->not_found = 0;
                vv->no_cacheable = 0;

                vv->data = val;
                vv->len = len;
            }

            v->set_handler(r, vv, v->data);

            return 0;
        }

    if (v->flags & NGX_HTTP_VAR_INDEXED) {
        vv = &r->variables[v->index];

            ...

        } else {
            vv->valid = 1;
            vv->not_found = 0;
            vv->no_cacheable = 0;

            vv->data = val;
            vv->len = len;
        }

从代码中可以看出来我们需要首先通过变量名获取到变量(ngx_http_variable_t), 这部分是使用ngx_hash_find函数在导出的变量列表(cmcf->variables_hash)里面找的. 找到变量之后需要判断变量的属性, 例如是否只读等. 接着是根据set_handler成员来判断如何赋值. 当然我们从这个函数中也可以清楚为什么ngx.var.XXX无法动态创建Nginx变量的原因了.

试一试

既然学习了如何去设置变量, 我们就可以参照上面的模块自己写一个小模块试一试, 写个类似hello world的小模块, 代码就不贴了, 有兴趣的同学可以在这里看看.

在实际使用的过程中, 如果每次都是需要获取同一个变量, 可以先把对应变量的hash值保存下来, 就不需要重复计算了.

PPPPSSSS

  1. 冻玩要发新砖了, 期待下;)
  2. 恭喜kinhozin拿下2014JD冠军~
c, nginx

Comments