zoukankan      html  css  js  c++  java
  • nginx的脚本引擎(一)

    nginx的脚本的语法和shell是很像的,我大致看了一下觉得挺有意思的,就想写写记录一下。我没看过shell脚本的引擎,不知道nginx脚本引擎和shell脚本引擎像不像,但是我觉得nginx的脚本引擎有点像C和汇编。

    ngx_http_script_engine_t这个结构就代表了一段脚本,ip指向的是编译好的脚本,sp指向的是一块内存用来存储脚本运行的时候产生的一些中间值。ip/sp从名字看就已经很像汇编了instruction pointer/stack pointer指令寄存器和栈寄存器呀,当然是我瞎猜的,有时间的话可以查一下官方文档。代码段里的各个指令长度不一定相同。

    再来说说编译过程,编译过程是在nginx_http_script_engine_t建立之前执行的,我先画出了整个图是为了更好理解。举个set指令编译的的例子,比如你在脚本里有这样的代码set $foo helloworld,脚本编译的步骤如下:

    第一步:首先在cmcf->variables_keys和cmcf->variables里增加一个变量foo,这个变量是可写的。我之前写的nginx的变量系统里只说了变量的读取方法,差别不大。

    第二步:把ngx_http_script_value_code_t指令放到代码段里(code字段是一个回调函数,赋值成ngx_http_script_value_code),把ngx_http_script_var_code_t指令放到代码段里(code字段是一个回调函数,赋值成ngx_http_script_set_var_code)。

    第三步:http请求来的时候会在rewrite阶段按顺序执行ip指向的这一段代码,也就是执行ngx_http_script_value_code和ngx_http_script_set_var_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++;
    }
    
    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;
    }
    

    第一条指令把“helloworld”这个字符串放到了sp里,第二条指令把值从sp里取出来存到了变量系统的foo变量里,任务完成,看起来很简单。

    set指令还可以这样用set $foo $x$y,这就是所谓的变量插值,过程和上面这个类似,只不过第一条指令是先从变量系统里取出$x和$y的值,再放入sp里。

    其他指令和set指令的执行过程类似,把我看到的也写一下吧

    if指令:同样举个最简单的例子if( $host = "www.foo.com" ),编译的时候依次把ngx_http_script_var_code/ngx_http_script_value_code_t/ngx_http_script_equal_code/ngx_http_script_if_code四条指令放到代码段里。脚本运行的时候这几条指令的工作分别是ngx_http_script_var_code把变量host的值取出来放到sp里。ngx_http_script_value_code_t把字符串“www.foo.com”放到sp里。ngx_http_script_equal_code比较sp里存的两个值是否相等并把两个值清除掉,相等就在sp里写入“1”,不相等就写入“0”(比较完以后这两个值就没用了,清除掉这两个值并且写入结果很像C里函数调用的过程)。ngx_http_script_if_code检查sp里的值是不是“0”,不是“0”说明条件为真继续执行之后的脚本,是“0”说明条件为假就会跳过这一段代码执行ngx_http_script_if_code_t结构里next偏移之后的代码。所有的代码都是在一个代码段里,不会因为有if把代码做嵌套,只不过会用next跳来跳去。

    有一点需要注意如果if在location里if体里可以做一些location的配置,比如root之类的。当NGX_HTTP_REWRITE_PHASE阶段执行脚本的时候会把新的loc_conf赋值给r->loc_conf,这个一定要注意是NGX_HTTP_REWRITE_PHASE阶段而不是NGX_HTTP_FIND_CONFIG_PHASE阶段,设置loc_conf一般情况是在NGX_HTTP_FIND_CONFIG_PHASE阶段,但是这次不是。

    void
    ngx_http_script_if_code(ngx_http_script_engine_t *e)
    {
        ngx_http_script_if_code_t  *code;
    
        code = (ngx_http_script_if_code_t *) e->ip;
    
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
                       "http script if");
    
        e->sp--;
    
        if (e->sp->len && (e->sp->len != 1 || e->sp->data[0] != '0')) {
            if (code->loc_conf) {
                e->request->loc_conf = code->loc_conf;
                ngx_http_update_location_config(e->request);
            }
    
            e->ip += sizeof(ngx_http_script_if_code_t);
            return;
        }
    
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,
                       "http script if: false");
    
        e->ip += code->next;
    }
    

    return指令:这个是比较简单的,脚本执行到这个指令就直接返回了,参数了可以带数据,例如return 200 helloworld,此外还可以重定向return 302 http://www.nginx.org。

    break指令:粗暴的结束目前的脚本,但是有一点要注意,如果break指令在location里面,他并不会影响location其他字段的设置,因为他们在不同的阶段执行。比如说设置如下的配置文件

            location / {
                root html;
                break;
                index index.html;
            }
    

     这一点都不会影响你的index指令,他们不在同一阶段,index是在NGX_HTTP_FIND_CONFIG_PHASE阶段break是在NGX_HTTP_REWRITE_PHASE阶段,就像if指令里说的那样。

    rewrite指令:这个略显麻烦,但是道理是一样的,休息,明天接着写。

  • 相关阅读:
    linux权限及ntfs文件系统权限的知识
    传入字符串,计算字符串长度(用指针实现)
    第一话-简单工厂模式
    Java 多字段排序Comparator(兼容Date,Integer,Doubel,Long)
    关于退运美国转基因玉米含有MRI 162转基因成分的质疑
    Oracle Directory文件夹的知识
    ggplot ggplot2 画图
    Linux Shell文件差集
    linux ps aux 结果解释
    perl常用字符串函数
  • 原文地址:https://www.cnblogs.com/4a8a08f09d37b73795649038408b5f33/p/9985528.html
Copyright © 2011-2022 走看看