nginx源码分析之变量设计
544 次浏览
发表于 2020-08-31 14:36
转载

nginx的配置文件使用简单灵活,某些部分还具备脚本语言的特点,变量就是其中一个特色。本文将分析变量是如何设计实现的。


0. 什么是变量

脚本语言都有变量这个东西,其作用就是让内容可变,用名称代替可变的内容,所以变量具有赋值和取值的特点。nginx的变量跟php一样,以$开头。两种用法:

赋值:set $some_var nginx;

取值:$some_var;


1.整体设计

* 创建
所有变量只能在配置文件解析,也就是工作进程启动之前创建,有些是内置的变量,有些是自定义的变量。没什么区别,比如 $http_host是内置变量,set $some_var some_val。通过set指定创建的是自定义变量,当然也可以其它方式,如果你自己写模块的话。这时用到 cmcf->variables_keys, cmcf->variables 两个数组,数组元素类型为ngx_http_variable_t。

* 使用
变量使用(即拿它的值)要先获取索引(发生在配置阶段),这是为了加快访问速度,然后根据索引拿它的值(发生在运行阶段)。这时用到r->variables数组,数组元素为ngx_http_variable_value_t(ngx_variable_value_t的别名)。
配置时:n = ngx_http_get_variable_index(cf, name);
运行时:v = ngx_http_get_indexed_variable(r, n);

cmcf->variables_keys 创建的变量都存在这个数组
cmcf->varialles 使用的变量(为了拿索引)都存储在这里。nginx会在init conf时检查cmcf->variables的所有变量必须在cmcf->variables_keys里。
r->variables 将cmcf->variables导入到这里,为了更方便处理,这时只需要拿值


2、创建变量

nginx有内置的变量,分布在好多个模块里,这些变量在配置解析之前构建完成,接下来解析配置,可能碰到变量(内置或自定义),最后处理所有的变量。

preconfigure : 添加内置变量,比如 $request, $server_name, $args 等。

parse             : 添加自定义变量。

init                 : 处理所有变量,比如自定义变量没有对应相同的内置变量,当作错误处理。

如果最终处理成功,会有个hash存储这些变量的数据。我们看下内部如何实现的:

* preconfigure阶段:

cmcf->variables_keys:所有内置变量都会添加到这个数组里。

每个成员的结构体是ngx_http_variable_t

typedef struct {

    ngx_str_t                     name;   /* must be first to build the hash */

    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_variable_t; 

name是名称,set_handler和get_handler分别用于赋值和取值,需要配合data,这几个比较简单。index是索引的意思,可以通过根据这个值拿到对应的变量,具体后面会再讲到。

flags是变量标记,不同的标记使其用法和用途不同,有NGX_HTTP_VAR_CHANGEABLE,NGX_HTTP_VAR_NOCACHEABLE标记。有NGX_HTTP_VAR_CHANGEABLE标记意味着变量是可变的。比如 $server_name是不可变的,你不能这样操作 set $server_name "err"; $args是可变的,就可以这样操作 set $args "ok"; 内置变量在源码里都有指定它的标记,自定义变量都是可变的。

3.使用变量

要获取nginx的变量的值有两种方式:索引和变量名

*索引
如前面介绍,先在配置阶段拿索引,然后在运行阶段根据索引拿值


*变量名
ngx_http_variable_value_t * ngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key);
在整个配置文件解析处理后,nginx会构造一个hash:cmcf->variables_hash,存储所有的变量。可想而知,根据变量名就可以快速拿到对应的变量。当然用索引的方式更快,至于选哪种视情况而定了。


4. 总结

要理解变量,要先理解nginx的两个阶段,解析阶段和运行阶段。解析阶段尽量做事前工作,如创建变量、拿索引等。到了运行阶段就可以快速的拿变量的值。还可以看出nginx的一个重要设计,解析时函数的参数基本有 ngx_conf_t *cf,到了运行阶段就是 ngx_http_request_t *r了。使用到的东西也更简化了,比如变量在解析阶段,需要有get和set(可选),但到了运行阶段,只需要拿值。这种细微的设计变化,可以好好思考,并转化成自己的理解。我经常推荐看nginx源码的同学可以从ngx_log_t *log这个东西入手,因为它反映了整个nginx的生命周期。


如果您觉得不错,就打赏支持一下吧〜
已有 1 人进行打赏
  • 皮皮鲁
发表评论
发表者

洪志道

NGINX官方团队成员

  • 11

    文章

  • 1

    关注

  • 34

    粉丝

活动推荐
版权所有©F5 Networks,Inc.保留所有权利。京ICP备16013763号-5