我眼中的 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)内容:(注 ...
随机推荐
- Android Gradle使用总结
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/77678577 本文出自[赵彦军的博客] 其他 Groovy 使用完全解析 http ...
- Ocelot中文文档-授权
Ocelot支持基于声明的授权. 这意味着如果您有要授权的路由,您可以将以下内容添加到您的ReRoute配置中. "RouteClaimsRequirement": { " ...
- 手把手教你全家桶之React(二)
前言 上一篇已经讲了一些react的基本配置,本遍接着讲热更新以及react+redux的配置与使用. 热更新 我们在实际开发时,都有用到热更新,在修改代码后,不用每次都重启服务,而是自动更新.并而不 ...
- web.config中的configSource
在大型项目中,可能存在第三方类库的配置如:log4.net,AOP框架Unity,WCF等,或是自定义的配置,造成web.config内容过多,不易维护,影响Config初始化. 这时我们可以使用co ...
- 什么是C语言。C语言入门
C语言是一种通用计算机编程语言,应用广泛. C语言的设计目标是提供一种编程语言,它可以编译,处理低级内存,生成少量机器代码,并以简单的方式运行,而无需任何操作环境的支持.虽然C语言提供了许多低级处理功 ...
- seek()对中文偏移测试
当前目录下创建"中文测试.txt"文件,写入: 我是大好人aaa我是大坏人bbb f = open('中文测试.txt', 'r+', encoding='utf-8') # f. ...
- Mego(05) - Mego for Visual Studio Extension
前言 可能对于一个新的框架而言使用入门对于陌生人而言是比较困难的,因此为了最大限度的为使用者提供便利性,我们给Mego框架开发了针对Visual Studio的集成开发工具,让大家可以像使用Entit ...
- Python_字符串的大小写变换
''' lower().upper().capitalize().title().swapcase() 这几个方法分别用来将字符串转换为小写.大写字符串.将字符串首字母变为大写.将每个首字母变为大写以 ...
- C Primer Plus 第10章 数组和指针 编程练习
这章感觉好难啊,放个别人的总结. // 多维数组和指针 #include <stdio.h> int main(void) { int zippo[4][2] = {{2, 4}, {6, ...
- C Primer Plus 第6章 C控制语句:循环 编程练习
记录下写的最后几题. 14. #include <stdio.h> int main() { double value[8]; double value2[8]; int index; f ...