nginx的配置文件使用简单灵活,某些部分还具备脚本语言的特点,变量就是其中一个特色。本文将分析变量是如何设计实现的。
0.什么是变量
脚本语言都有变量这个东西,其作用就是让内容可变,用名称代替可变的内容,所以变量具有赋值和取值的特点。nginx的变量跟php一样,以$开头。两种用法:
赋值:set$some_varnginx;
取值:$some_var;
1.整体设计
*创建
所有变量只能在配置文件解析,也就是工作进程启动之前创建,有些是内置的变量,有些是自定义的变量。没什么区别,比如 h t t p h o s t 是 内 置 变 量 , s e t http_host是内置变量,set httphost是内置变量,setsome_varsome_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会在initconf时检查cmcf->variables的所有变量必须在cmcf->variables_keys里。
r->variables 将cmcf->variables导入到这里,为了更方便处理,这时只需要拿值
2、创建变量
nginx有内置的变量,分布在好多个模块里,这些变量在配置解析之前构建完成,接下来解析配置,可能碰到变量(内置或自定义),最后处理所有的变量。
preconfigure:添加内置变量,比如 r e q u e s t , request, request,server_name,$args等。
parse:添加自定义变量。
init:处理所有变量,比如自定义变量没有对应相同的内置变量,当作错误处理。
如果最终处理成功,会有个hash存储这些变量的数据。我们看下内部如何实现的:
*preconfigure阶段:
cmcf->variables_keys:所有内置变量都会添加到这个数组里。
每个成员的结构体是ngx_http_variable_t
typedefstruct{
ngx_str_tname;/*mustbefirsttobuildthehash*/
ngx_http_set_variable_ptset_handler;
ngx_http_get_variable_ptget_handler;
uintptr_tdata;
ngx_uint_tflags;#这个比较重要,看下面解释
ngx_uint_tindex;
}ngx_http_variable_t;
name是名称,set_handler和get_handler分别用于赋值和取值,需要配合data,这几个比较简单。index是索引的意思,可以通过根据这个值拿到对应的变量,具体后面会再讲到。
flags是变量标记,不同的标记使其用法和用途不同,有NGX_HTTP_VAR_CHANGEABLE,NGX_HTTP_VAR_NOCACHEABLE标记。有NGX_HTTP_VAR_CHANGEABLE标记意味着变量是可变的。比如 s e r v e r n a m e 是 不 可 变 的 , 你 不 能 这 样 操 作 s e t server_name是不可变的,你不能这样操作set servername是不可变的,你不能这样操作setserver_name"err"; a r g s 是 可 变 的 , 就 可 以 这 样 操 作 s e t args是可变的,就可以这样操作set args是可变的,就可以这样操作setargs"ok";内置变量在源码里都有指定它的标记,自定义变量都是可变的。
3.使用变量
要获取nginx的变量的值有两种方式:索引和变量名
*索引
如前面介绍,先在配置阶段拿索引,然后在运行阶段根据索引拿值
*变量名
ngx_http_variable_value_t*ngx_http_get_variable(ngx_http_request_t*r,ngx_str_t*name,ngx_uint_tkey);
在整个配置文件解析处理后,nginx会构造一个hash:cmcf->variables_hash,存储所有的变量。可想而知,根据变量名就可以快速拿到对应的变量。当然用索引的方式更快,至于选哪种视情况而定了。
4.总结
要理解变量,要先理解nginx的两个阶段,解析阶段和运行阶段。解析阶段尽量做事前工作,如创建变量、拿索引等。到了运行阶段就可以快速的拿变量的值。还可以看出nginx的一个重要设计,解析时函数的参数基本有ngx_conf_tcf,到了运行阶段就是ngx_http_request_tr了。使用到的东西也更简化了,比如变量在解析阶段,需要有get和set(可选),但到了运行阶段,只需要拿值。这种细微的设计变化,可以好好思考,并转化成自己的理解。我经常推荐看nginx源码的同学可以从ngx_log_t*log这个东西入手,因为它反映了整个nginx的生命周期。
相关推荐:java架构师培训:如何对NginxIngress进行内核参数调优
如果你想了解更多关于java架构师的专业知识,可以加入JAVA架构师交流群:1037935907,里面都是同行,有资源分享包括但不限于(分布式架构、高可扩展、高性能、高并 发、Jvm性能调优、Spring,MyBatis,Nginx源码分析,Redis,ActiveMQ、、Mycat、Netty、Kafka、Mysql 、Zookeeper、Tomcat、Docker、Dubbo、Nginx)。欢迎一到五年的工程师加入,合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!