大牛:http://www.cnblogs.com/zengkefu/p/5563608.html
当请求来临的时候,NGINX会选择进入虚拟主机,匹配location后,进入请求处理阶段。
在请求处理阶段中,发生跳转时,会重新匹配location,再次进入请求处理阶段。
在请求处理阶段中,发生跳转时,会重新匹配location,再次进入请求处理阶段。
===================================================================
1、模块与指令的关系
每个模块包括多个指令,每个指令都有相应的处理函数
在运行时,NGINX会解析配置文件,按照配置指令进行模块的加载并注册指令处理函数到请求处理的不同阶段。
也就是说,在请求处理的不同阶段,会调用配置文件中的配置指令对应的处理函数。
特别要强调的是,在内容处理阶段,只可以包含一个模块的指令处理函数。
注意配置指令时,可以查看指令处理函数运行的阶段。Map指令不与任何阶段关联,是声明性的。
2、变量的种类
索引变量
未索引变量具有值访问函数,用户访问时,NGINX开始解析URL参数,并非所有具有值访问函数的变量都会缓存。$arg_*的变量是实时求值
3、变量的求值
具有取访问函数的变量,不一定具有缓存。具有缓存的只有在第一次访问变量的时候,会求值。
NGX——MAP模块的map指令,给自定义变量定义取处理函数,并且具有缓存功能。
map $A $B {
default 0;
x 1;
}
解读:当变量$A为x时,$B就取值1,默认为default,$B就取值1,一般定义在Server块外部
非缓存变量,解析配置求值
set $A $B;立即计算赋值
4、变量与请求的关系
变量是与请求绑定的,每个请求都具有变量的独立副本,各个请求间的变量不会相互影响。
NGX——AUTH——REQUEST模块指令 auth_request是个特例,跳专到的子请求可以共享父请求的变量。
请求分为主请求和子请求或子子请求。
特殊情况
并非所有的变量都是绑定一个请求,$request_method,$request_url都是横跨父子请求。
第三方模块提供了解决方案echo_request_method
5、NGINX内置变量局限性
$arg_xxx无法取得多个值,只会取得第一个,
使用 ngx_lua 模块提供的 ngx.req.get_uri_args 函数解决
未定义变量或未发现时,处理为空字符串,不能判断是否为未定义或未发现
NGX_LUA模块解决
location /test {
content_by_lua '
if ngx.var.arg_name == nil then
ngx.say("name: missing")
else
ngx.say("name: [", ngx.var.arg_name, "]")
end
‘
}
$cookie_XXX 变量也会在名为XXX
的 cookie 不存在时返回特殊值“没找到”:
location /test {
content_by_lua '
if ngx.var.cookie_user == nil then
ngx.say("cookie user: missing")
else
ngx.say("cookie user: [", ngx.var.cookie_user, "]")
end
';
}
6、指令与处理阶段
------rewrite------
页面跳转
网站跳转
1)set
2) set_unescape_uri
set_by_lua $c "return ngx.var.a + ngx.var.b";
在 Lua 代码里读取ngx.var.a
和ngx.var.b
时得到的其实都是 Lua 字符串类型的值"32"
和"56"
;
接着,我们对两个字符串作加法运算会触发 Lua 对加数进行自动类型转换(Lua 会把两个加数先转换为数值类型再求和);
然后,我们在 Lua 代码中把最终结果通过return
语句返回给外面的 Nginx 变量$c
;
最后,ngx_lua 模块在给$c
实际赋值之前,也会把return
语句返回的数值类型的结果,也就是 Lua 加法计算得出的“和”,自动转换为字符串
在运行时,不同模块的配置指令集之间的先后顺序一般是不确定的(严格来说,一般是由模块的加载顺序决定的,但也有例外的情况)。
比如A
和B
两个模块都在rewrite
阶段运行指令,于是要么是A
模块的所有指令全部执行完再执行B
模块的那些指令,
要么就是反过来,把B
的指令全部执行完,再去运行A
的指令。除非模块的文档中有明确的交待,否则用户一般不应编写依赖于此种不确定顺序的配置。
3)第三方模块 ngx_headers_more 提供了一系列配置指令,用于操纵当前请求的请求头和响应头。
其中有一条名叫 more_set_input_headers 的指令可以在rewrite
阶段改写指定的请求头(或者在请求头不存在时自动创建)。
这条指令总是运行在rewrite
阶段的末尾
? location /test {
? set $value dog;
? more_set_input_headers "X-Species: $value";
? set $value cat;
? echo "X-Species: $http_x_species";
? }
$http_XXX 变量在匹配请求头时会自动对请求头的名字进行归一化,即将名字的大写字母转换为小写字母,
同时把间隔符(-
)替换为下划线(_
),所以变量名 $http_x_species
才得以成功匹配 more_set_input_headers 语句中设置的请求头 X-Species
.
--------------------endrewrite-----------------------
--------------------access---------------------------
1)ngx_access 提供的 allow 和 deny 配置指令可用于控制哪些 IP 地址可以访问,哪些不可以
location /hello { allow 127.0.0.1; deny all; echo "hello world"; }
。ngx_access 模块自己的多条配置指令之间是按顺序执行的,直到遇到第一条满足条件的指令就不再执行后续的 allow 和 deny 指令。
如果首先匹配的指令是 allow,则会继续执行后续其他模块的指令或者跳到后续的处理阶段;
而如果首先满足的是 deny 则会立即中止当前整个请求的处理,并立即返回403
错误页。
2)ngx_lua 模块提供了配置指令 access_by_lua,用于在access
请求处理阶段插入用户 Lua 代码。
这条指令运行于access
阶段的末尾,因此总是在 allow 和 deny 这样的指令之后运行,虽然它们同属access
阶段。
一般我们通过 access_by_lua 在 ngx_access 这样的模块检查过客户端 IP 地址之后,
再通过 Lua 代码执行一系列更为复杂的请求验证操作,比如实时查询数据库或者其他后端服务,以验证当前用户的身份或权限。
location /hello {
access_by_lua ' if ngx.var.remote_addr == "127.0.0.1" then return end ngx.exit(403) '; echo "hello world";
}
--------------------endacess-------------------------
--------------------content--------------------------
1)echo 指令
2)echo_exec
3)proxy_pass
4)echo_location
5)content_by_lua
content
阶段属于一个比较靠后的处理阶段,运行在先前介绍过的 rewrite
和 access
这两个阶段之后。当和 rewrite
、access
阶段的指令一起使用时,这个阶段的指令总是最后运行,例如:
绝大多数 Nginx 模块在向 content
阶段注册配置指令时,本质上是在当前的 location
配置块中注册所谓的“内容处理程序”(content handler)。
每一个 location
只能有一个“内容处理程序”,因此,当在 location
中同时使用多个模块的 content
阶段指令时,只有其中一个模块能成功注册“内容处理程序”。
? location /test {
? echo hello;
? content_by_lua 'ngx.say("world")';
? }
这里,ngx_echo 模块的 echo 指令和 ngx_lua 模块的 content_by_lua 指令同处 content
阶段,
于是只有其中一个模块能注册和运行这个 location
的“内容处理程序”:
并非所有模块的指令都支持在同一个 location
中被使用多次,例如 content_by_lua 就只能使用一次,所以下面这个例子是错误的:
? location /test {
? content_by_lua 'ngx.say("hello")';
? content_by_lua 'ngx.say("world")';
? }
正确的写法应当是:
location /test {
content_by_lua 'ngx.say("hello") ngx.say("world")';
}
ngx_proxy 模块的 proxy_pass 指令和 echo 指令也不能同时用在一个location
中,因为它们也同属content
阶段。
不少 Nginx 新手都会犯类似下面这样的错误:
? location /test {
? echo "before...";
? proxy_pass http://127.0.0.1:8080/foo;
? echo "after...";
? }
?
? location /foo {
? echo "contents to be proxied";
? }
正确的方法:
配置指令 echo_before_body 和 echo_after_body 之所以可以和其他模块运行在content
阶段的指令一起工作,是因为它们运行在 Nginx 的“输出过滤器”中。
前面我们在 (一) 中分析 echo 指令产生的“调试日志”时已经知道,Nginx 在输出响应体数据时都会调用“输出过滤器”,
所以 ngx_echo 模块才有机会在“输出过滤器”中对 ngx_proxy 模块产生的响应体输出进行修改(即在首尾添加新的内容)。
值得一提的是,“输出过滤器”并不属于 (一) 中提到的那 11 个请求处理阶段(毕竟许多阶段都可以通过输出响应体数据来调用“输出过滤器”) location /test {
echo_before_body "before...";
proxy_pass http://127.0.0.1:8080/foo;
echo_after_body "after...";
}
location /foo {
echo "contents to be proxied";
}
重点注意:
当在内容处理阶段,没有注册成功的内容处理程序,那么NGINX 会默认把请求依次交给三个内容处理模块去处理,只要有一个处理,就掠过后续。
A、ngx_index
location / {
root html;
index index.html index.htm:
autoindex on;
}
当没有任何匹配时,就匹配根,index处理函数只对/感兴趣,如果不是就交给下个autoindex处理
index处理函数开始接手,去检查html/是否存在index.html,如果存在就发起内部跳转/index.html,重新匹配location;
由于index处理函数只对/感兴趣,所以把请求转发给autoindex处理函数处理,autoindex也只对 /感兴趣,它就把请求交给static处理
B、ngx_autoindex
只对/感兴趣,index在根下html下找不到index.html时,就回把请求转交给autoindex处理,会生成一个目录文件索引网页返回给客户端
当非/时,把请求交给static处理
autoindex on;
C、ngx_static
这是内容处理阶段最后一个静态模块,所有静态文件都交给这个处理函数,返回给客户端静态资源。
--------------------endcontent-----------------------