点赞
评论
收藏
分享
举报
nginx源码分析之变量设计
发表于2020-08-31 14:36

浏览 1.8k

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的生命周期。


已修改于2023-03-09 02:05
创作不易,留下一份鼓励
洪志道

暂无个人介绍

关注



写下您的评论
发表评论
全部评论(0)

按点赞数排序

按时间排序

关于作者
洪志道
这家伙很懒还未留下介绍~
11
文章
0
问答
54
粉丝
相关文章
引子我是NGINXUnit的贡献者,Unit是我非常喜欢的一个开源软件。我将写一系列Unit的文章分享Unit的世界,相信这个优秀的软件会有非常好的前景。NGINXUnit是什么?NGINXUnit是一个全新的,由NGINX作者亲自设计,带领NGINX核心团队开发的纯c软件。官方的定义:Unit是一个动态的web和应用服务器。因此它的三大核心为:动态,web和应用。Unit总体架构后续会有专门文章分析Unit构架设计,敬请关注。动态动态指两部分,动态配置和应用进程的动态管理。这里只介绍动态配置,这是它最大的亮点之一。动态配置一直是NGINX软件的缺陷,重新设计的Unit没有这个问题。简单说,Unit已经没有配置文件。Unit提供了httpAPI接口,所有配置的更新都通过RESTful方式操作。应用Unit是个多语言应用软件,它支持同时多个语言,甚至同个语言的不同版本,比如python2和python3,php5和php7。NGINX还有个问题,它不支持应用开发。是的,lua模块已经能做非常多的应用了。但是官方想支持更多主流的语言,于是有了这个设计。webUnit已经支持了sta
点赞 6
浏览 1.6k
原文作者:NGINX中文社区官方团队ofF5原文链接:NGINXSprint年度线上会议:报名通道已开启,立即预定您的NGINX深潜之旅转载来源:NGINX官方网站 带上您的潜水服、调节器、潜水电脑表和水下摄像机,跟随我们在NGINXSprintChina2022年度线上会议期间,一起深潜到NGINX的斑斓世界吧! 12月1日,在一年一度的NGINXSprintChina2022线上大会中了解最热的行业趋势以及NGINX的最新动态,并探索NGINX及其周边生态的使用案例、技术解析和运维实践,您还将有机会与NGINX官方团队、行业大咖以及社区中的开发者和技术爱好者共同探讨交流。 关于NGINXSprintChina2022NGINXSprintChina2022是一年一度NGINXSprint全球线上大会的本地分支版本,也是F5NGINX中国区全年最盛大的旗舰会议。在去年的Sprint大会中,我们曾一起遨游太空,而今年正如您所见——我们将一起深潜到海底。 此次会议将于20
点赞 0
浏览 901
事件驱动架构非常强大,非常适合用在分布式微服务环境中。事件驱动架构提供了解耦的架构、更容易实现的可伸缩性和更高程度的弹性。 但是,与请求和应答类型的架构相比,正确使用事件驱动架构要困难得多。 在过去的
点赞 0
浏览 449