Nginx(五) break,if,return,rewrite和set指令的执行顺序深究

本文深入探讨了 Nginx 中 break, if, return, rewrite 和 set 指令的执行顺序。在 server 层,指令按配置顺序执行,若执行 break 或 rewrite ... break/last 后会根据 URI 匹配 location。return 指令会立即结束请求处理并返回指定状态码和 URI。在 location 层,break 和 rewrite ... break 会终止当前 location 的重写循环,而 rewrite ... last 则会立即匹配下一个 location。循环重写不超过 10 次,超过会引发 500 错误。" 87868825,8180645,C#与AutoCAD二次开发实战,"['C#编程', 'AutoCAD开发', '二次开发', 'ObjectARX', '数据库扩展']
摘要由CSDN通过智能技术生成

       本篇文章主要对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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值