本篇文章主要对break,if,return,rewrite和set这5个指令的执行顺序进行深究,如需了解这5个指令的功能和配置,请参考另一篇文章 Nginx(三) 配置文件详解
最终结论
由于文章篇幅较长,所以我就先把结论贴出来,再看测试结果。
1.server层ngx_http_rewrite_module模块相关指令按照配置顺序依次执行;
2.server层执行完break指令后,该层级所有跟ngx_http_rewrite_module模块相关的指令都不再被执行,但不影响location层ngx_http_rewrite_module模块相关指令的继续执行,所以请求处理过程会继续向下;
3.location层执行完break指令后,当前server所有层级跟ngx_http_rewrite_module模块相关的指令都不再被执行(换言之就是,最终URI就这吧,别再找了),nginx根据最终请求路径将请求结果返回给客户端;
4.无论在哪个层级执行完return指令后,请求处理过程都会立刻终止并将指定的code、重定向URL或响应正文文本返回给客户端。
5.虽然Nginx在循环重写过程中最多是可以执行11次循环,但在第11次循环中执行rewrite后会抛500异常,所以严格意义上来说,正常情况下Nginx请求重写循环过程最多就是10次。强烈建议亲自做下测试10。
6.rewrite regex /xxx break指令与单独break指令的中断效果一样,只是执行完重写后再执行中断,类似于先执行rewrite regex /xxx,再执行break。
7.rewrite regex /xxx last指令是执行完重写后,立即开始(下一轮)匹配location,server层或当前location层其它指令不再执行。
执行顺序
说完上面的结论后,我们再来重新总结下break,if,return,rewrite和set这5个指令的执行顺序,总体分为两大步骤。
第一步:按序执行。server层按照配置循序依次执行,请求该重写重写,且无次数限制。server层指令按序执行完毕后进入第二步,根据URI匹配location,开始请求重写循环。在此过程中,如果server层执行了return指令,请求处理过程立刻终止并将指定code和URI或正文返回给客户端;如果server层执行了break或rewrite regex /xxx break/last指令,立即结束第一步,根据URI匹配location,开始请求重写循环。
第二步:循环执行。根据第一步确认的URI去匹配location,匹配到location后,进入到对应的location中按序执行该模块下的指令。如果请求又被重写,则再匹配location,循环执行上述任务(循环过程不能超过10次),直到找到最终URI。最后再根据最终URI处理请求并将结果返回给客户端。在此过程中,如果执行了return指令,请求处理过程立刻终止并将指定code和URI或正文返回给客户端;如果执行了break或rewrite regex /xxx break指令,循环终止,根据最后一次重写的URI处理请求并将结果返回给客户端;如果执行了rewrite regex /xxx last指令,会立即开始匹配下一个location,当前location下的其它指令不再被执行。
说完结论和执行顺序后,我们再将原文3.3章节的所有知识结合起来一起做个测试。
基本配置如下
http {
log_subrequest on; # 开启将子请求日志记录到access.log中
log_format format2 escape=json '{'
'"SN":"$sn",' #自定义变量sn
'"http_host":"$http_host",'
'"remote_addr":"$remote_addr",'
'"time_iso8601":"$time_iso8601",'
'"request":"$request",'
'"http_referer":"$http_referer",'
'"request_time":"$request_time",'
'"request_length":"$request_length",'
'"status":"$status",'
'"bytes_sent":"$bytes_sent",'
#'"body_bytes_sent":"$body_bytes_sent",'
'"user_agent":"$http_user_agent",'
'}';
absolute_redirect on;
server_name_in_redirect off;
port_in_redirect on;
server {
listen 8688;
server_name www.read*********.cn;
access_log logs/access.log format2;
error_log logs/error.log notice; # 将error_log日志级别修改为notice,否则rewrite log无法记录。
rewrite_log on; # 开启记录请求重写日志,默认是关闭
root pages; # 根目录设置为psges,该目录下有index.html、test.html、one.html、two.html、three.html
# 下面配置本次测试的指令
···
···
}
}
测试1:server层rewrite前被break
server {
···
break;
rewrite /t1 /test.html;
set $sn 1; # 该变量最终值将记录到access.log中。
if ($sn = 1) {
set $sn 2;
break;
set $sn 3;
}
return 500;
location / {
index index.html index.htm;
}
}
请求地址 | host:8688/t1 |
请求结果 | 404 |
error.log 日志输出 |
*878 open() "/usr/local/nginx/pages/t1" failed (2: No such file or directory) |
access.log 日志输出 |
{"SN":"" |
最终地址 | host:8688/t1 |
结论 | 1.在请求重写指令未执行前就执行了break,导致server层级下所有跟ngx_http_rewrite_module模块相关的指令都不再执行,所以请求并未重写,请求路径不变。if、set、return指令都未执行。 2.location中未匹配到/t1,且pages文件夹下没有t1文件,所以返回404。 |
测试2:server层rewrite前被return
server {
···
return 500;
rewrite /t1 /test.html;
set $sn 1; # 该变量最终值将记录到access.log中。
if ($sn = 1) {
set $sn 2;
break;
set $sn 3;
}
return 500;
location / {
index index.html index.htm;
}
}
请求地址 | host:8688/t1 |
请求结果 | 500 |
error.log 日志输出 |
无 |
access.log 日志输出 |
{"SN":"" |
最终地址 | |
结论 | server层rewrite前return被执行后,请求处理过程立刻终止,并将指定code返回给客户端。 |
测试3:server层rewrite完被break
server {
···
rewrite /t1 /test.html;
set $sn 1; # 该变量最终值将记录到access.log中。
if ($sn = 1) {
set $sn 2;
break;
set $sn 3;
}
return 500;
location / {
index index.html index.htm;
}
}
请求地址 | host:8688/t1 |
请求结果 | 200 |
error.log 日志输出 |
无 |
access.log 日志输出 |
{"SN":"2" |
最终地址 | host:8688/test.html |
结论 | 请求被重写,执行完break后,"set $sn 3"和"return"指令未执行。 server层,ngx_http_rewrite_module 模块相关指令按照配置顺序依次执行。 server层执行break后,该层级所有跟ngx_http_rewrite_module模块相关的指令都不再执行。 |
2023/11/17 23:36:31 [notice] 11070#11070: *169 "/t1" matches "/t1", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:36:31 [notice] 11070#11070: *169 rewritten data: "/test.html", args: "", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
测试4:server层rewrite完被return
server {
···
rewrite /t1 /test.html;
return 500;
set $sn 1; # 该变量最终值将记录到access.log中。
location / {
index index.html index.htm;
}
}
请求地址 | host:8688/t1 |
请求结果 | 500 |
error.log 日志输出 |
无 |
access.log 日志输出 |
{"SN":"" |
最终地址 | |
结论 | server层rewrite完return被执行后,请求处理过程立刻终止,并将指定code返回给客户端。 |
2023/11/17 23:38:45 [notice] 11140#11140: *172 "/t1" matches "/t1", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:38:45 [notice] 11140#11140: *172 rewritten data: "/test.html", args: "", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:38:45 [warn] 11140#11140: *172 using uninitialized "sn" variable while logging request, client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
测试5:location层rewrite完被break
server {
···
rewrite /t1 /t2;
rewrite /t2 /t3;
set $sn 1; # 该变量最终值将记录到access.log中。
if ($sn = 1) {
set $sn 2;
break;
set $sn 3;
}
location /t2 {
rewrite /t2 /test.html;
}
location /t3 {
rewrite /t3 /t4;
if ($sn = 2) {
set $sn 31;
rewrite /t4 /t5;
break;
set $sn 32;
rewrite /t5 /t6;
}
set $sn 33;
rewrite /t5 /p1;
rewrite /t6 /p2;
}
location /t5 {
set $sn 51;
rewrite /t5 /p3;
}
location /t6 {
set $sn 61;
rewrite /t6 /p4;
}
location / {
index index.html index.htm;
}
}
请求地址 | host:8688/t1 |
请求结果 | 404 |
error.log 日志输出 |
*173 open() "/usr/local/nginx/pages/t5" failed (2: No such file or directory) |
access.log 日志输出 |
{"SN":"31" |
最终地址 | host:8688/t5 |
结论 | 1.server或location层ngx_http_rewrite_module模块相关的指令正常情况下按照配置顺序依次执行; 2.server层执行break后,该层级所有跟ngx_http_rewrite_module模块相关的指令都不再执行,但不影响location层ngx_http_rewrite_module模块相关指令的执行。 3.location层执行完break指令后,请求重写循环过程结束,break执行前确认的URI就是最终URI,nginx根据最终地址处理请求并结果返回给客户端。 |
2023/11/17 23:40:19 [notice] 11208#11208: *173 "/t1" matches "/t1", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:40:19 [notice] 11208#11208: *173 rewritten data: "/t2", args: "", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:40:19 [notice] 11208#11208: *173 "/t2" matches "/t2", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:40:19 [notice] 11208#11208: *173 rewritten data: "/t3", args: "", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:40:19 [notice] 11208#11208: *173 "/t3" matches "/t3", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:40:19 [notice] 11208#11208: *173 rewritten data: "/t4", args: "", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:40:19 [notice] 11208#11208: *173 "/t4" matches "/t4", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:40:19 [notice] 11208#11208: *173 rewritten data: "/t5", args: "", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:40:19 [error] 11208#11208: *173 open() "/usr/local/nginx/pages/t5" failed (2: No such file or directory), client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
测试6:location层rewrite完被return
server {
···
rewrite /t1 /t2;
rewrite /t2 /t3;
set $sn 1; # 该变量最终值将记录到access.log中。
if ($sn = 1) {
set $sn 2;
break;
set $sn 3;
}
location /t2 {
rewrite /t2 /test.html;
}
location /t3 {
rewrite /t3 /t4;
return 502;
if ($sn = 2) {
set $sn 31;
rewrite /t4 /t5;
break;
set $sn 32;
rewrite /t5 /t6;
}
set $sn 33;
rewrite /t5 /p1;
rewrite /t6 /p2;
}
location /t5 {
set $sn 51;
rewrite /t5 /p3;
}
location /t6 {
set $sn 61;
rewrite /t6 /p4;
}
location / {
index index.html index.htm;
}
}
请求地址 | host:8688/t1 |
请求结果 | 502 |
error.log 日志输出 |
无 |
access.log 日志输出 |
{"SN":"2" |
最终地址 | |
结论 | location层rewrite完return被执行后,请求处理过程会立刻终止,并将指定code返回给客户端。 |
2023/11/17 23:42:08 [notice] 11282#11282: *174 "/t1" matches "/t1", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:42:08 [notice] 11282#11282: *174 rewritten data: "/t2", args: "", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:42:08 [notice] 11282#11282: *174 "/t2" matches "/t2", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:42:08 [notice] 11282#11282: *174 rewritten data: "/t3", args: "", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:42:08 [notice] 11282#11282: *174 "/t3" matches "/t3", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
2023/11/17 23:42:08 [notice] 11282#11282: *174 rewritten data: "/t4", args: "", client: 14.145.163.156, server: www.read*********.cn, request: "GET /t1 HTTP/1.1", host: "www.read*********.cn:8688"
测试7:location层rewrite前被break
server {
···
set $sn 1;
rewrite /t0 /t1;
break;
rewrite /t1 /p1;
location /t1 {
set $sn 11;
rewrite /t1 /t2;
}
location /t2 {
set $sn 22;
break;
rewrite /t2 /t3;
}
location /t3 {
set $sn 33;
rewrite /t3 /test.html;
}
location /p1 {
set $sn 500;
return 500;
}
location / {
index index.html index.htm;
}
}