我眼中的 Nginx(三):Nginx 变量和变量插值
张超:又拍云系统开发高级工程师,负责又拍云 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+Systemd托管netcore应用
以前dotnet web应用程序开发完成后,我们都是使用IIS部署在Windows Server上,如今netcore技术发展迅速,因为其跨平台的特性,将dotnet web应用程序部署在更方便部署和 ...
- Nginx(三):日志文件管理
一.Nginx日志描述 通过访问日志,你可以得到用户地域来源.跳转来源.使用终端.某个URL访问量等相关信息: 通过错误日志,你可以得到系统某个服务或server的性能瓶颈等.因此,将日志好好利用,你 ...
- linux 安装nginx -查看 linux的环境变量
我发现在linux上面安装linux很简单 在CentOS release 6.5 上面先看一下操作系统的版本: lsb_release -a 直接执行 yum install nginx 系统自动的 ...
- Nginx教程(三) Nginx日志管理
Nginx教程(三) Nginx日志管理 1 日志管理 1.1 Nginx日志描述 通过访问日志,你可以得到用户地域来源.跳转来源.使用终端.某个URL访问量等相关信息:通过错误日志,你可以得到系统某 ...
- nginx(三)反向代理和负载均衡
nginx(三)反向代理和负载均衡 正向代理概念:比如在学校要上网,在学校内网是一个内网ip,需要连上公网就需要一个正向代理服务器. 反向代理概念: 看下图(Nginx只做请求的转发,后台有多个htt ...
- 【nginx】nginx配置文件结构,内置变量及参数调优
Nginx的配置文件是一个纯文本文件,它一般位于Nginx安装目录的conf目录下,整个配置文件是以block的形式组织的.每个block一般以一个大括号“{”来表示.block 可以分为几个层次,整 ...
- 使用正则表达式来截取nginx中的内置变量
nginx 中的内置变量都可以通过 if 指令 + 正则表达式来进行截取,截取之后的结果通过正则表达式的分组来进行引用 比如:从请求中传过来的一个名为 ssl_client_s_dn 的变量,它的值是 ...
- Nginx三种模式的虚拟主机(附Apache基于域名的虚拟主机)
1.安装nginx # pcre中文"perl兼容正则表达式",安装pcre库是为了让nginx支持具备URL重写功能 # 的Rewrite模块,rewrite可以实现动态页面转成 ...
- nginx多层反代配置变量proxy_set_header
Nginx多层反代配置变量proxy_set_header过程记录 第一层代理: (1)路径: $ vim /data/soft/nginx/conf/vhost/xixi.conf (2)内容:(注 ...
随机推荐
- ASP.NET Core Api网关Ocelot的中文文档
架构图 入门 不支持 配置 路由 请求聚合 GraphQL 服务发现 微服务ServiceFabric 认证 授权 Websockets 管理 流量控制 缓存 QoS服务质量 转换Headers 转换 ...
- es6(六):module模块(export,import)
es6之前,社区模块加载方案,主要是CommonJS(用于服务器)和AMD(用于浏览器) 而es6实现的模块解决方案完全可以替代CommonJS和AMD ES6模块设计思想:尽量静态化,在编译时就能确 ...
- 函数上下文this
一般来说谁调用上下文都指向谁,具体有以下几种情况: 1.函数用圆括号调用,函数的上下文是window 注意:所有的全局变量都是window的属性,而函数里边定义的变量谁的属性也不是. 2.函数作为对象 ...
- Python_语法和界面设计
http://www.runoob.com/python/python-gui-tkinter.html http://www.python-course.eu/python_tkinter.php
- TCP/IP Http 和Https socket之间的区别
TCP/IP Http 和Https socket之间的区别 TCP/IP是个协议组,它分为网络层,传输层和应用层, 在网络层有IP协议.ICMP协议.ARP协议.RARP协议和BOOTP协议. ...
- JAVA Set 交集,差集,并集
/** * Created by yuhui on 2017/7/11 0011. */ import java.util.HashSet; import java.util.Set; public ...
- 16个必须熟悉的linux服务器监控命令
本原创文章属于<Linux大棚>博客. 博客地址为http://roclinux.cn. 文章作者为roc. == 原文:16 Linux Server Monitoring Comman ...
- 二十一、Hadoop学记笔记————kafka的初识
这些场景的共同点就是数据由上层框架产生,需要由下层框架计算,其中间层就需要有一个消息队列传输系统 Apache flume系统,用于日志收集 Apache storm系统,用于实时数据处理 Spark ...
- Notify和NotifyAll的区别?
Notify和NotifyAll都是用来对对象进行状态改变的方式,只是他们的作用域不太一样,从字面上就能看的出来,当对象被上锁之后,当其他的方法要去访问该对象中的数据,就需要该对象对其进行解锁,当然, ...
- jQuery上下滑动内容切换选项卡
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...