一 Nginx变量
Nginx 的'配置文件'使用的就是一门'微型的编程语言'
在 'nginx 配置中','变量'只能存放'一种类型的值',因为也只存在一种类型的值,那就是'字符串'
(1)设置变量
举例:'nginx.conf 文件'中有下面这一行配置:
set $var "hello world";
特点:我们看到,'Nginx 变量名'前面'有一个 $ 符号',这是'语法上'的要求
强调:所有的 'Nginx 变量'在 Nginx 配置文件中'引用时都须带上 $ 前缀',这种表示方法和'Perl、PHP' 这些语言是'相似'的
标准 ngx_rewrite 模块的 set 配置指令进行了赋值操作
(2)变量插值构造新字符串
① 把变量嵌入到字符串常量中以构造出新的字符串
set $a hello;
set $b "$a, $a";
解释:通过'已有的 Nginx 变量 $a' 的值来'构造'变量 $b 的值,于是'这两条指令顺序'执行完之后,$a 的值是hello,而 $b 的值则是 hello, hello.
备注:这种技术在 Perl 世界里被称为"变量插值"(variable interpolation),它让专门的'字符串拼接'运算符
② 使用第三方 ngx_echo 模块的 echo 配置指令
强调:'并非'所有的配置指令都支持"变量插值",事实上'指令参数'是否允许"变量插值",'取决于'该指令的实现模块
+++++++++++'分割线'+++++++++++
需求:将 $foo 变量的值作为'当前请求的响应体'输出
我们看到,'echo 配置指令'的参数也支持"变量插值"
备注:echo模块'默认没有加载',需要'自己编译'然后配置
server {
listen 8080;
location /test {
set $foo hello;
# 需要安装ngx_echo模块
echo "foo: $foo";
}
}
③ 消除歧义
server {
listen 8080;
location /test {
set $first "hello ";
echo "${first}world";
}
}
说明:当'引用的变量名'之后'紧跟着变量名'的构成字符时(比如后跟'字母、数字以及下划线'),我们就需要使用'特别的记法'来'消除歧义'
解释:在 echo 配置指令的参数值中'引用变量 $first' 的时候,后面'紧跟着 world' 这个单词,所以如果直接写作 "$firstworld" 则 Nginx "变量插值"计算引擎会将之'识别为引用了变量 $firstworld'
解决策略:为了解决这个难题,Nginx 的字符串记法支持'使用花括号在 $ 之后把变量名'围起来
④ 不创建变量直接使用
set 指令不仅有'赋值'的功能,它还有'创建 Nginx 变量的副作用',即当作为'赋值对象'的变量尚'不存在'时,它会'自动创建'该变量
++++++++++++++++'分割线'++++++++++++++++
set $b "$a, $a";
举例:如果 $a 这个变量'尚未创建',则 set 指令'会自动'创建 $a 这个'用户变量',如果我们'不创建'就'直接使用'它的值则会'报错'
server {
listen 8080;
location /bad {
# 注意这个配置的'报错' -->'不创建'则'直接使用'
echo $foo;
}
}
++++++++++++++++'报错提示'++++++++++++++++
此时 Nginx 服务器会'拒绝'加载配置: '[emerg] unknown "foo" variable'
代理转发:proxy_set_header是用来'设置请求头'的,设置了请求头后,'后端服务器'就可以'获取'到这些变量值
备注1: Host、X-Real-IP、X-Forwarded-For -->'标准的'HTTP请求头-->'RFC规范'定义
备注2: 可以'自定义'HTTP请求头
+++++++++++++++++'设置方式'+++++++++++++++++
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host; #$proxy_host
proxy_set_header X-NginX-Proxy true;
proxy_set_header Connection "";
⑥ 细节
Nginx '变量的创建'和'赋值操作'发生在全然'不同的时间阶段'
1)Nginx '变量的创建'只能发生在 'Nginx 配置加载'的时候,或者说 'Nginx 启动'的时候;
2)而'赋值操作'则只会发生在'请求实际处理'的时候;
小结:这意味着'不创建而直接使用变量'会导致'启动失败',同时也意味着我们'无法在请求处理时动态地'创建新的 Nginx 变量
⑦ 变量作用域
Nginx 变量一旦创建,其'变量名的可见范围'就是'整个 Nginx 配置',甚至可以'跨越不同虚拟主机'的 server配置块
+++++++++++++'案例讲解'+++++++++++++
server {
listen 8080;
location /foo {
echo "foo = [$foo]";
}
location /bar {
set $foo 32;
echo "foo = [$foo]";
}
}
'location /bar' 中用 set 指令创建了变量 $foo,于是在'整个配置文件中'这个变量都是可见的,因此我们可以在 'location /foo' 中'直接引用'这个变量而'不用担心 Nginx 会报错'
+++++++++++++++++'结论'+++++++++++++++++
自己理解:变量与请求强相关,如果在'location'定义变量,其它location也'可以直接引用',但是变量值为'空串'
Nginx 变量名的'可见范围'虽然是整个配置,但'每个请求'都有'所有变量的独立副本',或者说都有各变量用来存放值的容器的'独立副本',彼此'互不干扰'
比如:'上面的案例'我们请求了/bar 接口后,$foo 变量被赋予了值 32,但它'丝毫不会影响'后续对 /foo 接口的请求所对应的 $foo 值(它'仍然是空'的!),因为各个请求都'有自己独立的 $foo 变量的副本'
重点:Nginx 变量在请求之间'不是全局共享'的东西、或者说'全局变量','Nginx 变量的生命期'是'不可能跨越请求'边界的
(2)内置变量
① 官方内置变量文档
以"$"开头的变量都是'内置变量'
② 常用的内置变量
nginx 模块提供的"预定义变量",或者说'内建变量'(builtin variables)
$remote_addr;
存放了'客户端的地址',注意是客户端的公网IP,也就是一家人访问一个网站,则会显示为'路由器的公网IP'
$args;
变量中存放了'请求行中(GET请求)的参数',例如http://node101.yinzhengjie.org.cn/main/index.do?'id=20190221&partner=search'中的id=20190221&partner=search
$document_root;
保存了针对'当前资源的请求'的'系统根目录',如/apps/nginx/html -->对应'root'指令
$document_uri;
保存了当前请求中'不包含指令的URI',注意是'不包含请求'的指令,比如http://node101.yinzhengjie.org.cn/main/index.do?id=20190221&partner=search会'被定义为/main/index.do'
$host;
存放了'请求的host名称'
$http_user_agent;
'客户端浏览器'的详细信息
$http_cookie;
'客户端的cookie'信息
limit_rate 10240;
echo $limit_rate;
如果nginx服务器使用limit_rate配置了'显示网络速率',则会显示,如果'没有设置',则'显示0'
$remote_port;
客户端请求Nginx服务器时'随机打开的端口',这是每个'客户端自己的端口'
$remote_user;
已经'经过Auth Basic Module验证'的用户名
$request_body_file;
做'反向代理'时发给'后端服务器的本地资源'的名称
$request_method;
请求'资源的方式',GET、PUT、DELETE等
$request_filename;
当前'请求的资源文件的路径'名称,由root或alias指令与URI请求生成的文件'绝对路径',如:/apps/nginx/html/main/index.html
$request_uri;
包含'请求参数'的原始URI,'不包含主机名',如:/main/index.do?id=20190221&partner=search
$scheme;
请求的'协议',如ftp、https、http等
$server_protocol;
保存了'客户端请求资源'使用的协议的版,如HTTP/1.0、'HTTP/1.1'、HTTP/2.0等
$server_addr;
保存了'服务器的IP地址'
$server_name;
请求的'服务器的主机名'
$server_port;
请求的'服务器的端口号'
+++++++++++++++'补充知识点'+++++++++++++++
$arg_name 请求中的'name参数'-->请求的参数的名字,根据'具体情况'而定
$binary_remote_addr 远程地址的'二进制'表示
$body_bytes_sent 已发送的'消息体'字节数
$content_length HTTP请求信息里的"Content-Length"
$content_type 请求信息里的"Content-Type"
$host 请求信息中的"Host",如果请求中没有Host行,则等于设置的服务器名
$http_referer '引用地址'
$http_via '最后'一个访问服务器的'ip地址'
$is_args 如果请求行'带有参数',返回"?",否则'返回空字符串'
$nginx_version 当前运行的nginx版本号
$pid 'worker进程'的PID
$query_string 与$args'相同'
$realpath_root 按root指令或alias指令算出的'当前请求的绝对路径',其中的'符号链接'都会解析成真是文件路径
$request '用户请求'
$request_body 这个变量(0.7.58+)包含'请求的主要信息',在使用proxy_pass或fastcgi_pass指令的location中'比较有意义'
$request_body_file 客户端'请求主体'信息的'临时文件名'
$request_completion 如果'请求成功',设为"OK";如果'请求未完成'或者'不是'一系列请求中最后一部分则设为空
$request_filename 当前请求的文件路径名,比如/opt/nginx/www/test.php
$request_uri 请求的URI,'带参数'; 比如http://localhost:88/test1/
$uri 请求的URI,可能'和最初的值有不同',比如经过'重定向'之类的
$http_name 用来获取'任意请求头'的值
③ 服务端打印nginx内置变量并返回给客户端
++++++++++++++++++'调试使用的'++++++++++++++++++
location /main {
index index.html;
default_type text/html;
echo "remote_addr = $remote_addr";
echo "******";
echo "args = $args";
echo "******";
echo "document_root = $document_root";
echo "******";
echo "document_uri = $document_uri";
echo "******";
echo "host = $host";
echo "******";
echo "http_user_agent = $http_user_agent";
echo "******";
echo "http_cookie = $http_cookie"
echo "******";
limit_rate 10240;
echo "limit_rate = $limit_rate";
echo "******";
echo "remote_prot = $remote_port";
echo "******";
echo "remote_user = $remote_user";
echo "******";
echo "request_body_file = $request_body_file";
echo "******";
echo "request_method = $request_method";
echo "******";
echo "request_filename = $request_filename";
echo "******";
echo "request_uri = $request_uri";
echo "******";
echo "scheme = $scheme";
echo "******";
echo "server_protocol = $server_protocol";
echo "******";
echo "server_addr = $server_addr";
echo "******";
echo "server_name = $server_name";
echo "******";
echo "server_port = $server_port";
echo "******";
}