张超:又拍云系统开发高级工程师,负责又拍云 CDN 平台相关组件的更新及维护。Github ID: tokers,活跃于 OpenResty 社区和 Nginx 邮件列表等开源社区,专注于服务端技术的研究;曾为 ngx_lua 贡献源码,在 Nginx、ngx_lua、CDN 性能优化、日志优化方面有较为深入的研究。

如果读者曾配置过 Nginx,那么一定知道 Nginx 允许我们在配置文件里嵌入”变量”,这些变量由 Nginx 的各个模块定义,其目的是为了提升配置的灵活性,如这一段配置:

location = /t {
set $my_addr "127.0.0.1:8081";
proxy_pass http://$my_addr/index.html;
}

  

我们可以通过操作变量 $my_addr 来动态指定 upstream。

认识 Nginx 变量

Nginx 的变量和 perl、php 等语言的类似,由美元符号 $ 开头,随后跟着一个字符串,代表这个变量的名称,例如 $name,可选地,这个字符串可以用花括号包围,譬如 ${name} 。在 Nginx 世界里,合法的变量名可用字符集为 [a-zA-Z0-9_],特别地,Nginx 支持正则子组,即 $1,$2 这样的变量。注意变量值只有字符串这一种类型。

另外还存在一类特殊的变量,它们的名称是不固定的,更确切地说,它们描述的是一群变量。譬如 $http_,描述对应请求的请求头;$sent_http 则描述对应请求的响应头。

在实现上,一个变量由其名称、读/取处理程序和一些标志位组成。这些标志位定义了变量的属性,例如表明一个变量是否可缓存、是否可进行索引。

变量拥有两种存放方式,第一种是储存在一个全局的 hash 表里,使用该变量时需要进行查询;第二种则是储存在一个全局动态数组里,每个变量存在一个唯一的索引。相比较而言,第二种方式在性能上更加优秀,因为它不需要计算 hash key,也不需要查找 hash 表。

变量解析是运行时的。即惰性求值,而且通常变量解析总是和请求绑定的,因此变量信息虽然只有一份,但是变量值却会有多份。

前面提到,变量是可缓存的,Nginx 模块开发者们可以视情况决定变量的可缓存性,如果一个变量设定为可缓存,则它在经历第一次求值后,其求值结果会被缓存起来,后续使用到该变量时会直接获取到缓存里的值。譬如,Nginx 的 map 模块就把变量设置为可缓存,因为在它看来这样的一次变量获取操作是足够昂贵的。

谨慎对待变量缓存。笔者曾遇到过一个因为变量缓存引起的问题: $host 这个变量,该变量也是可缓存的。根据文档里的描述,这个变量的取值顺序为:

in this order of precedence: host name from the request line, or host name from the “Host” request header field, or the server name matching a request

通常情况下,我们可以使用 $host 获取到请求头里的 Host,然而因为它的可缓存性,该变量无法体现 Host 头部的改动,比如下面的代码:

local old_host = ngx.var.host
ngx.req.set_header("Host", "foo.example.com")
local new_host = ngx.var.host

new_host的值将和 old_host 一样,而非直觉上所想的 http://foo.example.com。

另外,在原生 Nginx 的实现里,主/子请求共享同一份变量缓存。如果子请求使用了某个变量,并且该变量值被缓存下来,那么主请求之后访问该变量时,就会得到子请求所产生的缓存拷贝。当然,如果模块开发者自己定义子请求的创建,也是可以绕过这一点的。譬如 ngx_lua 在创建子请求的 API 里就提供了是否指定主/子请求共享变量的选项。

变量插值

用户的需求总是多变的。譬如在使用 upstreamcache 功能(另外一个典型的例子就是 access_log)时,通常需要设计一个良好的缓存 key,此时需要考虑到的因素可能有多个,即我们的 key 不会单单由一个变量或者常量组成,而是需要设计成它们的结合体,如 mykey=$http_host&$uri&$args。

在实现上,Nginx 首先会把包含变量和常量的复杂字符串转换成一种中间形式。具体来说,Nginx 会把这个字符串按变量和常量进行拆分,分别为它们进行“包装”(包括设置好对应的取值回调函数),之后按顺序放入一个动态数组,这个动态数组就是中间形式了。

比如上段提到的缓存 key,经过处理后,得到的动态数组是这样的:

拆分的处理是在具体指令解析阶段完成的。真正取值发生在具体请求的运行当中,通过执行该动态数组每个成员的取值回调函数,将单一的值拼装在一起,最终就实现了变量插值。

发散

借鉴 Nginx 的变量插值,笔者不久前用 Golang 实现了一个小玩意(tokers/corgi),当然,还是为了好玩 :)。

变量插值的思想非常值得学习,如果能利用好这一点,程序设计上就会变得更加灵活。

 

推荐阅读:

我眼中的 Nginx(一):Nginx 和位运算​

我眼中的 Nginx(三):Nginx 变量和变量插值的更多相关文章

  1. 快速掌握Nginx(三) —— Nginx+Systemd托管netcore应用

    以前dotnet web应用程序开发完成后,我们都是使用IIS部署在Windows Server上,如今netcore技术发展迅速,因为其跨平台的特性,将dotnet web应用程序部署在更方便部署和 ...

  2. Nginx(三):日志文件管理

    一.Nginx日志描述 通过访问日志,你可以得到用户地域来源.跳转来源.使用终端.某个URL访问量等相关信息: 通过错误日志,你可以得到系统某个服务或server的性能瓶颈等.因此,将日志好好利用,你 ...

  3. linux 安装nginx -查看 linux的环境变量

    我发现在linux上面安装linux很简单 在CentOS release 6.5 上面先看一下操作系统的版本: lsb_release -a 直接执行 yum install nginx 系统自动的 ...

  4. Nginx教程(三) Nginx日志管理

    Nginx教程(三) Nginx日志管理 1 日志管理 1.1 Nginx日志描述 通过访问日志,你可以得到用户地域来源.跳转来源.使用终端.某个URL访问量等相关信息:通过错误日志,你可以得到系统某 ...

  5. nginx(三)反向代理和负载均衡

    nginx(三)反向代理和负载均衡 正向代理概念:比如在学校要上网,在学校内网是一个内网ip,需要连上公网就需要一个正向代理服务器. 反向代理概念: 看下图(Nginx只做请求的转发,后台有多个htt ...

  6. 【nginx】nginx配置文件结构,内置变量及参数调优

    Nginx的配置文件是一个纯文本文件,它一般位于Nginx安装目录的conf目录下,整个配置文件是以block的形式组织的.每个block一般以一个大括号“{”来表示.block 可以分为几个层次,整 ...

  7. 使用正则表达式来截取nginx中的内置变量

    nginx 中的内置变量都可以通过 if 指令 + 正则表达式来进行截取,截取之后的结果通过正则表达式的分组来进行引用 比如:从请求中传过来的一个名为 ssl_client_s_dn 的变量,它的值是 ...

  8. Nginx三种模式的虚拟主机(附Apache基于域名的虚拟主机)

    1.安装nginx # pcre中文"perl兼容正则表达式",安装pcre库是为了让nginx支持具备URL重写功能 # 的Rewrite模块,rewrite可以实现动态页面转成 ...

  9. nginx多层反代配置变量proxy_set_header

    Nginx多层反代配置变量proxy_set_header过程记录 第一层代理: (1)路径: $ vim /data/soft/nginx/conf/vhost/xixi.conf (2)内容:(注 ...

随机推荐

  1. Ocelot中文文档-负载均衡

    Ocelot能通过可用的下游服务对每个ReRoute进行负载平衡. 这意味着您可以扩展您的下游服务,并且Ocelot可以有效地使用它们. 可用的负载均衡器的类型是: LeastConnection - ...

  2. DDGScreenShot—图片擦除功能

    写在前面 图片擦除功能,也是运用图片的绘制功能, 将图片绘制后,拿到相应的图片.当然,有一涨底图更明显 实现代码如下 /** ** 用手势擦除图片 - imageView --传图片 - bgView ...

  3. 印钞机 V1.0(量化选基总结)

    今年的元旦,在家把之前手工的选基方法完全程序化了.这是我的"印钞机" V1.0. 为什么叫印钞机,详细情况可见下文及最后的总结. 量化选基成果 我的主要基金投资方法其实就是量化选基 ...

  4. Spring框架碰壁日常更新

    关于Spring环境搭建遇到的问题:

  5. SOFA 源码分析 — 预热权重

    前言 SOFA-RPC 支持根据权重对服务进行预热功能,具体地址:预热权重. 引用官方文档: 预热权重功能让客户端机器能够根据服务端的相应权重进行流量的分发.该功能也常被用于集群内少数机器的启动场景. ...

  6. System.Drawing.image 与ImageSource 互转

    private BitmapSource bs(Bitmap bt) { IntPtr ip = bt.GetHbitmap(); BitmapSource bitmapSource = System ...

  7. windows下安装mysql-5.7.11-winx64

    1.解压.   2.将『D:\Program Files\mysql-5.7.11-winx64\bin』加入系统环境变量.   3.修改my-default.ini.   4.初始化data目录,在 ...

  8. Python和Java的硬盘夜话

    这是一个程序员的电脑硬盘,在一个叫做"学习"的目录下曾经生活着两个小程序,一个叫做Hello.java,即Java小子:另外一个叫做hello.c ,也就是C老头儿. C老头儿的命 ...

  9. DX11 Without DirectX SDK--04 使用DirectX Tool Kit帮助开发

    回到 DirectX11--使用Windows SDK来进行开发 DirectX Tool Kit下载 DirectX Tool Kit是一个包含许多类的集合,用于为公共Windows平台编写Dire ...

  10. vue国际化高逼格多语言

    ## 1.NPM 项目安装 ``` cnpm i vue-i18n ``` ## 2.使用方法 ``` /* 国际化使用规则 */ import Vue from 'vue' import VueI1 ...