编程技术分享平台

网站首页 > 技术教程 正文

「系统架构」Nginx调优之变量的使用(3)

xnh888 2024-10-11 20:28:13 技术教程 26 ℃ 0 评论


在上一篇文章「系统架构」Nginx调优之变量的使用(2)中我们介绍了自定义变量和内置变量,下面我们继续接着介绍Nginx中变量的可见性和动态内置变量。

变量的可见性

nginx中的变量虽然不全是全局变量,但每一个变量都是全局可见的。所谓全局可见,是指不管变量定义在配置文件的哪个地方,它在整个配置文件中都是可见的,但这个并不表示他是全局变量。

举个例子:

location /a {
    return 200 “I am $a”;
}

location /b {
   set $a “b”;
   return 200 “I am $a”;
}

在这个例子中,第一个location中的变量“$a”既不是自定义变量也不是内置变量,按照之前的知识,此时的nginx应该是无法启动的。

而第二个location中,可以看到用set指令定义了一个变量“$a”,从语法上看这是一个合法的配置,所以它是可以正常启动的。那如果把这两个location放在同一个配置文件中,nginx是不是可以正常启动呢?

答案是肯定的,原因就是nginx中的变量是全局可见的,第一个location中的变量“$a”看到了第二个location中对它的定义。那它又不是全局变量又是怎么回事呢?我们用curl访问以下第二个location:

curl http://127.0.0.1/b

打印结果是:

I am b

这个结果应该是毫无疑问的。

现在不确定的应该是访问第一个location的时候应该出现什么结果,如果变量“$a”是一个全局变量,那很显然它的值应该也是“b”。但它不是全局变量,那应该是什么值呢?用curl测试一下:

curl http://127.0.0.1/a

打印结果是:

I am

从表面上看,此时变量“$a”应该是空字符或者空格之类非可见性字符,但是因为在当前的例子中,变量“$a”的前后不存在可见的字符,导致没办法区分此时变量“$a”到底是个什么内容。

现在我们把第一个locaiton例子稍微改动一下:

location /a {
?? return 200 “I am -->$a<--”;
}

在变量前后都加入了可视的字符,然后再用curl测验一下:

curl http://127.0.0.1/a

结果如下:

I am --><--

通过结果可以推断出变量“$a”变成了一个空字符,这个现象其实间接的说明了变量“$a”在nginx并不是一个全局变量,因为它没有打印出b这个字符。

另外通过后台日志可以看到如下一条相关的日志信息:

[warn] 1733#0: *3 using uninitialized "a" variable,(这条日志只是节选了跟当前变量相关的信息)

日志说nginx正在使用一个未初始化的变量,该变量的名字是a。从这条日志看,nginx中的变量也有初始化这个概念。从变量“$a”的打印结果看,nginx会把未初始化的变量设置为空字符。

动态内置变量

所谓“动态”指的是变量的名字是不确定的,这个不确定性发生在nginx的运行过程中。比如,对一个http请求,同一个请求可以有不同的查询参数,而查询参数的不同又可以返回不同的结果,举个例子,有如下查询功能:

/query?name=xxx
/query?age=yyy

该查询功能有两个入参,一个是name,一个是age,当仅有name的时候返回所有名字是xxx的人;而当仅有age的时候返回所有年龄是yyy的人;当两个参数都存在的时候返回的是名字是xxx且年龄是yyy的人。当请求实际发生的时候,在nginx内部肯定可以解析出所有的查询入参和对应的值的,但是在配置文件中如何得个这个入参的值就比较费劲。有人可能会说,可以直接把入参名字做成内置变量名,比如,像如下这样:

location /query {
??? return 200?“$name and $age”;
}

看起来问题迎刃而解,可问题是nginx需要内置多少这种内置变量呢?

http中的查询参数是一个自定义行为,每个使用者都可以随意决定自己请求中的查询参数,即便同一个功能,有着同样意义的查询参数,查询参数的实际值也可以不一样。比如,上面的例子,完全可以把两个查询参数name和age替换为n和a,按照这种变化程度,nginx根本不可能完全猜测出用户对查询参数的定义,所以这种方案是行不通的。

nginx的解决方案是使用前缀的方式来表示http模块中各种动态内置变量,比如,上面例子中的两个查询入参name和age,可以分别用arg_name和arg_age来表示其对应的变量,而arg_就是查询参数中某个入参的变量前缀。如此一来nginx只需要在内部内置一个以arg_开头的规则就可以方便的表示这类数据了。

目前,在nginx的http模块中有六种内置动态变量,分别是“http_”、“sent_http_”、“upstream_http_”、“upstream_cookie”、“cookie_”,“arg_”。

1)以“http_”开头的动态内置变量可以表示http请求过程中的任意请求头,使用的过程中不区分大小写,并且请求头中如果有“-”字符需要用“_”字符替代。

我们先用curl去访问以下nginx的官方文档页,来看看请求过程中都发送了哪些请求头:

curl http://nginx.org/en/dosc/ -v

去掉其它部分,只保留请求头部分,打印结果如下:

> GET /en/docs HTTP/1.1
> User-Agent: curl/7.19.7(x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.0.0 zlib/1.2.3 libidn/1.18libssh2/1.4.2
>Host: nginx.org
> Accept: */*

可以看到有三个请求头,根据nignx的规则,在配置文件中获取这三个请求头值只需要在对应的请求头名字前加上“http_”前缀就可以了,示例如下:

location / {
??? return 200 “User-Agent: $http_user_agent ”;
}

用curl测是结果如下:

curl http://127.0.0.1/
User-Agent: curl/7.19.7(x86_64-redhat-linux-gnu) libcurl/7.19.7    NSS/3.14.0.0 zlib/1.2.3 libidn/1.18libssh2/1.4.2

同样,如果想获取其它两个请求头,使用$http_host和$http_accept就可以了。

2)以“sent_http_”开头的动态内置变量可以表示http响应过程中的任意响应头,规则跟“http_”动态内置变量一样。

使用如下的配置来看一下响应过程中包括哪些响应头:

location /a {
   return 200 “test sent_http_”;
}

用带-v参数的curl访问该资源:

curl http://127.0.0.1/a -v

仅保留响应头部分:

< Server: nginx/1.9.4
< Date: Sat, 21 Apr 2018 09:04:36 GMT
<Content-Type: application/octet-stream
<Content-Length: 15
< Connection: keep-alive

现在来看一个在配置文件中使用connection这个响应头的一个例子:

location /a {
    return 200 “I am $sent_http_connection”;
}

用curl测试一下,结果如下:

curl http://127.0.0.1/a
I amkeep-alive

看起来一切都很顺畅,貌似不管哪个响应头,加上对应的前缀就可以轻而易举的获取。那我们再换一个响应头验证一下,看看能不能获取content_length这个头的值:

location/a {
    return 200 “I am $sent_http_content_length”;
}

用curl验证一下:

curl http://127.0.0.1/a
I am

结果并没有像我们预想的那样,content_length这个响应头的内容不见了,翻阅nginx文档好像也没有不妥的地方,白纸黑字写上的规则怎么说不行就不行了呢?此时你可能怀疑是nginx的bug,遗憾的是并不是这样的。

出现这种现象是因为涉及到了nginx中http模块阶段执行的模式。实际运行的时候nginx把整个请求过程分成了多个阶段,各个阶段对应完成不同的功能,我们这里出现的情况是因为return这个指令对应的阶段运行时,用来设置content_length这个响应头内容的阶段还没有执行,所以出现了该响应头内容“不见了”的情况。

虽然nginx提供了这种动态获取变量值得功能,但并不是在任何时候都能取到这个值。

3)以“cookie_”开头的动态内置变量可以表示http请求过程中的某个cookie值。需要和“$http_cookie”这个内置变量(非动态内置变量)区分一下,它代表请求中整个cookie值,比如:

location/a {
? ? return 200 “cookie:$http_cookie”;
}??

使用curl模拟一下带cookie的请求:

curl http://127.0.0.1/a? -H “cookie:a=b;b=c”
cookie:a=b;b=c

可以看到,它把cookie名字是a和cookie名字是b的值都打印出来了。

而以“cookie_”开头的变量则代表某个实际cookie值,比如“$cookie_a”代表本次请求中cookie名字是a的对应值,一个获取某个cookie值的例子:

location/a {
? ? return200? “ cookie b 的值是 [$cookie_b]”
}

使用curl模拟带cookie的请求:

curl http://127.0.0.1/a -H “cookie:a=b;b=哈哈哈”

输出结果如下:

cookie b 的值是 [哈哈哈]

4)最后一个是以“arg_”开头的动态内置变量,用法跟以“cookie_”开头的变量类似,就不再赘述了。这里需要说的是在“变量的有效字符”小节中用到的“$arg_变量”这个变量,之前的配置例子是这样的:

location? /? {
? ? set? $arg_变量 “我是变量”;
? ? return? 200? “$arg_变量”;
}

这个配置输出的结果是“变量”而非我们认为的“我是变量”,这是因为“$arg_变量”并不是一个变量,而是变量“$arg_”和文本字符“变量”的一个拼接。在nginx中变量“$arg_”不代表任何入参值,它会被nginx转换成空字符,所以最终结果就是一个文本“变量”。

注意:“upstream_http_”和“upstream_cookie_”两个内置变量,需要涉及到额外的知识,为了不分散读者的注意力这里就不再介绍了。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表