现在的位置: 首页 > 综合 > 正文

nginx脚本引擎

2013年07月04日 ⁄ 综合 ⁄ 共 4608字 ⁄ 字号 评论关闭

前面讲了nginx的变量机制,今天来讲讲nginx的脚本引擎。我们以一个比较简单的例子来讲述nginx的脚本引擎。例如,我们自己定以一个变量:

set $file  t_a;

这个set指令就会调用ngx_http_rewrite_set函数,下面来看下ngx_http_rewrite_set:

static char *
ngx_http_rewrite_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_rewrite_loc_conf_t  *lcf = conf;
.....
 if (ngx_http_rewrite_value(cf, lcf, &value[2]) != NGX_CONF_OK) {
        return NGX_CONF_ERROR;
    }
.....
    vcode = ngx_http_script_start_code(cf->pool, &lcf->codes,
                                       sizeof(ngx_http_script_var_code_t));
    if (vcode == NULL) {
        return NGX_CONF_ERROR;
    }

    vcode->code = ngx_http_script_set_var_code;
    vcode->index = (uintptr_t) index;

    return NGX_CONF_OK;
}

这里仅仅是列出了与脚本引擎相关的代码,并且是与我们例子相关的。首先会调用ngx_http_rewrite_value这个函数,这个函数是做什么的,等下再看。接着会调用ngx_http_script_start_code从lcf->codes这个数组中获取sizeof(ngx_http_script_var_code_t)个元素,在下面为这个元素赋值。其实ngx_http_script_start_code获取的元素的大小都是一个字节,也就是说ngx_http_script_start_code其实就是给vcode分配空间。下面来看下ngx_http_rewrite_value这个函数的作用:

static char *
ngx_http_rewrite_value(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf,
    ngx_str_t *value)
{
.....
    n = ngx_http_script_variables_count(value);

    if (n == 0) {
        val = ngx_http_script_start_code(cf->pool, &lcf->codes,
                                         sizeof(ngx_http_script_value_code_t));
        if (val == NULL) {
            return NGX_CONF_ERROR;
        }

        n = ngx_atoi(value->data, value->len);

        if (n == NGX_ERROR) {
            n = 0;
        }

        val->code = ngx_http_script_value_code;
        val->value = (uintptr_t) n;
        val->text_len = (uintptr_t) value->len;
        val->text_data = (uintptr_t) value->data;

        return NGX_CONF_OK;
    }
.....
}

这个函数我们也之列出了与我们例子相关的代码。首先会调用ngx_http_script_start_code这个函数,这个函数的作用就是统计value中的变量数目(以$开头的字符串),我们这个例子中value为t_a,所以没有变量,也就是n==0.而if判断条件的逻辑与我们上讲述vcode是一样的。这里也就不啰嗦了。

脚本引擎构建成功后内存图如下:此图来自深入剖析nginx(高群凯编写)书中的。

自此我们的脚本引擎构建成功了。那我们怎么让脚本引擎跑起来呢?我们可以利用rewrite指令来让我们的脚本引擎跑起来,在配置文件中配置如下:

location /t{
       set $file t_a;
       rewrite ^(.*)$ /index.html?$file redirect;
       root html
}

这样只要访问t目录都会被重定向到根目录,并把$file作为参数带过去。重定向是在nginx的NGX_HTTP_REWRITE_PHASE阶段调用ngx_http_rewrite_handler函数,下面来看下这个函数:

static ngx_int_t
ngx_http_rewrite_handler(ngx_http_request_t *r)
{
.....
    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);

    if (rlcf->codes == NULL) {
        return NGX_DECLINED;
    }

    e = ngx_pcalloc(r->pool, sizeof(ngx_http_script_engine_t));
    if (e == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    e->sp = ngx_pcalloc(r->pool,
                        rlcf->stack_size * sizeof(ngx_http_variable_value_t));
    if (e->sp == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    e->ip = rlcf->codes->elts;
    e->request = r;
    e->quote = 1;
    e->log = rlcf->log;
    e->status = NGX_DECLINED;

    while (*(uintptr_t *) e->ip) {
        code = *(ngx_http_script_code_pt *) e->ip;
        code(e);
    }
.....
}

代码中rlcf->codes就是我们在构建脚本引擎中为vcode分配空间的lcf->codes,这个理解应该很简单。代码出现了一个新的类型(ngx_http_variable_value_t)。先不管这个新的类型。来看e->ip是获取了codes的元素的地址,而在while循环中把e->ip转换成ngx_http_script_code_pt*的类型,然后直接调用这个函数指针指向的函数。那这个函数是什么呢?我回到脚本引擎构建的时候,在ngx_http_rewrite_value有这么一句话:

val->code = ngx_http_script_value_code;

在ngx_http_rewrite_set也有这么一句话:

vcode->code = ngx_http_script_set_var_code;

我们来看下val和vcode的类型:

typedef struct {
    ngx_http_script_code_pt     code;
    uintptr_t                   value;
    uintptr_t                   text_len;
    uintptr_t                   text_data;
} ngx_http_script_value_code_t;

typedef struct {
    ngx_http_script_code_pt     code;
    uintptr_t                   index;
} ngx_http_script_var_code_t;

从这两个结构可以看出,第一个成员都是ngx_http_script_code_pt类型的code。刚好对应上面ngx_http_rewrite_handler中的:

    while (*(uintptr_t *) e->ip) {
        code = *(ngx_http_script_code_pt *) e->ip;
        code(e);
    }

这样刚好就可以调用val->code以及vcode->code了。而e->ip的移动都是在code中进行的。因为val,vcode的类型不一样,需要移动的偏移也是不一样的。所以e->ip移动的偏移由各自的类型决定。

从上面脚本引擎的内存图来看,是先调用val->code,来看下val->code的逻辑:

void
ngx_http_script_value_code(ngx_http_script_engine_t *e)
{
    ngx_http_script_value_code_t  *code;

    code = (ngx_http_script_value_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_value_code_t);

    e->sp->len = code->text_len;
    e->sp->data = (u_char *) code->text_data;

    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
                   "http script value: \"%v\"", e->sp);

    e->sp++;
}

其实就是把code的值付给e->sp。那code->text_len和code->text_data的值可以看ngx_http_rewrite_value。再来看vcode->code的逻辑:

void
ngx_http_script_set_var_code(ngx_http_script_engine_t *e)
{
    ngx_http_request_t          *r;
    ngx_http_script_var_code_t  *code;

    code = (ngx_http_script_var_code_t *) e->ip;

    e->ip += sizeof(ngx_http_script_var_code_t);

    r = e->request;

    e->sp--;

    r->variables[code->index].len = e->sp->len;
    r->variables[code->index].valid = 1;
    r->variables[code->index].no_cacheable = 0;
    r->variables[code->index].not_found = 0;
    r->variables[code->index].data = e->sp->data;

}

整个逻辑就是把e->sp的值付给r->variables[code->index]。其实从代码中可以看出来sp的操作有点类似与数据结构中的栈。ngx_http_script_value_code通过e->sp++压栈,ngx_http_script_set_var_code通过e->sp--出栈。有人会在想r->variables又是从哪里冒出来的。ngx_http_init_request有这么几句话:

    r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts
                                        * sizeof(ngx_http_variable_value_t));
    if (r->variables == NULL) {
        ngx_destroy_pool(r->pool);
        ngx_http_close_connection(c);
        return;
    }

r->varibales刚好就是ngx_http_variables_valut_t的类型。这样与cmcf->variables刚好形成key-value的结构,而且key和value在各自索引是对应起来的。

抱歉!评论已关闭.