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


           本篇文章主要对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章节的所有知识结合起来一起做个测试。

    基本配置如下

    1. http {
    2. log_subrequest on; # 开启将子请求日志记录到access.log中
    3. log_format format2 escape=json '{'
    4. '"SN":"$sn",' #自定义变量sn
    5. '"http_host":"$http_host",'
    6. '"remote_addr":"$remote_addr",'
    7. '"time_iso8601":"$time_iso8601",'
    8. '"request":"$request",'
    9. '"http_referer":"$http_referer",'
    10. '"request_time":"$request_time",'
    11. '"request_length":"$request_length",'
    12. '"status":"$status",'
    13. '"bytes_sent":"$bytes_sent",'
    14. #'"body_bytes_sent":"$body_bytes_sent",'
    15. '"user_agent":"$http_user_agent",'
    16. '}';
    17. absolute_redirect on;
    18. server_name_in_redirect off;
    19. port_in_redirect on;
    20. server {
    21. listen 8688;
    22. server_name www.read*********.cn;
    23. access_log logs/access.log format2;
    24. error_log logs/error.log notice; # 将error_log日志级别修改为notice,否则rewrite log无法记录。
    25. rewrite_log on; # 开启记录请求重写日志,默认是关闭
    26. root pages; # 根目录设置为psges,该目录下有index.html、test.html、one.html、two.html、three.html
    27. # 下面配置本次测试的指令
    28. ···
    29. ···
    30. }
    31. }

    测试1:server层rewrite前被break

    1. server {
    2. ···
    3. break;
    4. rewrite /t1 /test.html;
    5. set $sn 1; # 该变量最终值将记录到access.log中。
    6. if ($sn = 1) {
    7. set $sn 2;
    8. break;
    9. set $sn 3;
    10. }
    11. return 500;
    12. location / {
    13. index index.html index.htm;
    14. }
    15. }
    请求地址 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

    1. server {
    2. ···
    3. return 500;
    4. rewrite /t1 /test.html;
    5. set $sn 1; # 该变量最终值将记录到access.log中。
    6. if ($sn = 1) {
    7. set $sn 2;
    8. break;
    9. set $sn 3;
    10. }
    11. return 500;
    12. location / {
    13. index index.html index.htm;
    14. }
    15. }
    请求地址 host:8688/t1
    请求结果 500

    error.log

    日志输出

    access.log

    日志输出

    {"SN":""
    最终地址
    结论 server层rewrite前return被执行后,请求处理过程立刻终止,并将指定code返回给客户端。

    测试3:server层rewrite完被break

    1. server {
    2. ···
    3. rewrite /t1 /test.html;
    4. set $sn 1; # 该变量最终值将记录到access.log中。
    5. if ($sn = 1) {
    6. set $sn 2;
    7. break;
    8. set $sn 3;
    9. }
    10. return 500;
    11. location / {
    12. index index.html index.htm;
    13. }
    14. }
    请求地址 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模块相关的指令都不再执行。

    1. 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"
    2. 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

    1. server {
    2. ···
    3. rewrite /t1 /test.html;
    4. return 500;
    5. set $sn 1; # 该变量最终值将记录到access.log中。
    6. location / {
    7. index index.html index.htm;
    8. }
    9. }
    请求地址 host:8688/t1
    请求结果 500

    error.log

    日志输出

    access.log

    日志输出

    {"SN":""
    最终地址
    结论 server层rewrite完return被执行后,请求处理过程立刻终止,并将指定code返回给客户端。
    1. 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"
    2. 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"
    3. 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

    1. server {
    2. ···
    3. rewrite /t1 /t2;
    4. rewrite /t2 /t3;
    5. set $sn 1; # 该变量最终值将记录到access.log中。
    6. if ($sn = 1) {
    7. set $sn 2;
    8. break;
    9. set $sn 3;
    10. }
    11. location /t2 {
    12. rewrite /t2 /test.html;
    13. }
    14. location /t3 {
    15. rewrite /t3 /t4;
    16. if ($sn = 2) {
    17. set $sn 31;
    18. rewrite /t4 /t5;
    19. break;
    20. set $sn 32;
    21. rewrite /t5 /t6;
    22. }
    23. set $sn 33;
    24. rewrite /t5 /p1;
    25. rewrite /t6 /p2;
    26. }
    27. location /t5 {
    28. set $sn 51;
    29. rewrite /t5 /p3;
    30. }
    31. location /t6 {
    32. set $sn 61;
    33. rewrite /t6 /p4;
    34. }
    35. location / {
    36. index index.html index.htm;
    37. }
    38. }
    请求地址 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根据最终地址处理请求并结果返回给客户端。

    1. 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"
    2. 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"
    3. 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"
    4. 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"
    5. 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"
    6. 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"
    7. 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"
    8. 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"
    9. 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

    1. server {
    2. ···
    3. rewrite /t1 /t2;
    4. rewrite /t2 /t3;
    5. set $sn 1; # 该变量最终值将记录到access.log中。
    6. if ($sn = 1) {
    7. set $sn 2;
    8. break;
    9. set $sn 3;
    10. }
    11. location /t2 {
    12. rewrite /t2 /test.html;
    13. }
    14. location /t3 {
    15. rewrite /t3 /t4;
    16. return 502;
    17. if ($sn = 2) {
    18. set $sn 31;
    19. rewrite /t4 /t5;
    20. break;
    21. set $sn 32;
    22. rewrite /t5 /t6;
    23. }
    24. set $sn 33;
    25. rewrite /t5 /p1;
    26. rewrite /t6 /p2;
    27. }
    28. location /t5 {
    29. set $sn 51;
    30. rewrite /t5 /p3;
    31. }
    32. location /t6 {
    33. set $sn 61;
    34. rewrite /t6 /p4;
    35. }
    36. location / {
    37. index index.html index.htm;
    38. }
    39. }
    请求地址 host:8688/t1
    请求结果 502

    error.log

    日志输出

    access.log

    日志输出

    {"SN":"2"
    最终地址
    结论 location层rewrite完return被执行后,请求处理过程会立刻终止,并将指定code返回给客户端。
    1. 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"
    2. 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"
    3. 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"
    4. 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"
    5. 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"
    6. 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

    1. server {
    2. ···
    3. set $sn 1;
    4. rewrite /t0 /t1;
    5. break;
    6. rewrite /t1 /p1;
    7. location /t1 {
    8. set $sn 11;
    9. rewrite /t1 /t2;
    10. }
    11. location /t2 {
    12. set $sn 22;
    13. break;
    14. rewrite /t2 /t3;
    15. }
    16. location /t3 {
    17. set $sn 33;
    18. rewrite /t3 /test.html;
    19. }
    20. location /p1 {
    21. set $sn 500;
    22. return 500;
    23. }
    24. location / {
    25. index index.html index.htm;
    26. }
    27. }
  • 相关阅读:
    越流行的大语言模型越不安全
    【微服务】Day04
    Unity3D 基础——使用 Vector3.Distance 计算两个物体之间的距离
    信息学奥赛一本通:1164:digit函数
    服务器数据恢复-服务器硬盘指示灯黄灯闪烁的数据恢复案例
    [附源码]SSM计算机毕业设计疫情期间回乡人员管理系统JAVA
    「Verilog学习笔记」用3-8译码器实现全减器
    基于SpringBoot+Vue网上商城系统的设计与实现
    2022.03 Kali Linux Maltego工具
    Redis篇---第十二篇
  • 原文地址:https://blog.csdn.net/ShenDaiSun/article/details/134428468