设为首页 收藏本站
查看: 933|回复: 0

[经验分享] Nginx关于Rewrite执行顺序详解.docx

[复制链接]

尚未签到

发表于 2016-12-27 07:21:32 | 显示全部楼层 |阅读模式
 
Nginx关于Rewrite执行顺序详解
第一篇:break和last的区别
Rewrite 模块概述
REFER:  http://wiki.nginx.org/NginxHttpRewriteModule#rewrite
 
If the directives of this module are given at the server level,then they are carried out before the location of the request is determined. Ifin that selected location there are further rewrite directives, then they alsoare carried out. If the URI changed as a result of the execution of directivesinside location, then location is again determined for the new URI.
This cycle can be repeated up to 10 times, after which Nginxreturns a 500 error.
Rewrite(URL重写)指令可以出现在server{}下,也可以出现在location{}下,它们之间是有区别的!对于出现在server{}下的rewrite指令,它的执行会在location匹配之前;对于出现在location{}下的rewrite指令,它的执行当然是在location匹配之后,但是由于rewrite导致HTTP请求的URI发生了变化,所以location{}下的rewrite后的URI又需要重新匹配location,就好比一个新的HTTP请求一样(注意由location{}内的rewrite导致的这样的循环匹配最多不超过10次,否则nginx会报500错误)。总的来说,如果server{}和location{}下都有rewrite,依然是先执行server{},然后进行location匹配,如果被匹配的location{}之内还有rewrite指令,那么继续执行rewrite,同时因为location{}内的rewrite改变了URI,那么重写后的结果URI需要当做一个新的请求,重新匹配location(应该也包括重新执行server{}下的rewrite吧)。
Last与break flag的区别
关于last flag和break flag的区别,官方文档的描述是:“last - completes processingof rewrite directives, after which searches for corresponding URI and location”和“break - completes processingof rewrite directives”,都有“不让继续执行后面的rewrite指令”的含义,但是两者的区别并没有展开。
这里我用实验来告诉大家区别。实验准备:
1、  安装nginx;(如果对安装和location不了解的,请参考:http://eyesmore.iyunv.com/blog/1141660)
 
2、  在nginx安装目录的html子目录下创建4个文件,分别叫:aaa.html,bbb.html,ccc.html和ddd.html,文件内容分别是各自的文件名(例aaa.html文件内容不妨写aaa html file)。
3、  Nginx配置文件初始化是:
 
error_log logs/error.log info;  #URL重写模块的日志会写入此文件
 
  server {
       listen       9090;
       server_name  localhost;
       root html;
       rewrite_log on;  #打开URL重写模块的日志开关,以便写入error_log
   
       location  /aaa.html {
            rewrite"^/aaa\.html$"  /bbb.html;
            rewrite"^/bbb\.html$"  /ddd.html;
        }  
   
       location  /bbb.html {
            rewrite "^/bbb\.html$"/ccc.html;
        }  
}
上述配置注意两点:1、打开rewrite模块的日志开关,以便rewrite执行日志写入error_log(注:rewrite日志写入error_log的级别是notice,所以要注意error_log日志级别,此处用info);2、定义了两个location,分别是/aaa.html和/bbb.html,但是在/aaa.html中,把/aaa.html重写成/bbb.html,接着又把/bbb.html重写成/ddd.html;在/bbb.html中,把/bbb.html重写成/ccc.html。
 
[测试1] 没有last和break标记时:请求aaa.html
[iyunv@web108 ~]# curlhttp://localhost:9090/aaa.html
dddhtml file
[iyunv@web108 ~]#
Error_log的日志内容:
2011/08/07 22:13:23 [notice] 9066#0: *85 "^/aaa\.html$" matches "/aaa.html",client: 127.0.0.1, server: localhost, request: "GET /aaa.htmlHTTP/1.1", host: "localhost:9090"
2011/08/07 22:13:23 [notice] 9066#0: *85 rewritten data: "/bbb.html",args: "", client: 127.0.0.1, server: localhost, request: "GET/aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:13:23 [notice] 9066#0: *85 "^/bbb\.html$" matches"/bbb.html", client: 127.0.0.1, server: localhost, request:"GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:13:23 [notice] 9066#0: *85 rewritten data: "/ddd.html",args: "", client: 127.0.0.1, server: localhost, request: "GET/aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:13:23 [info] 9066#0: *85client 127.0.0.1 closed keepalive connection
 
URL重写模块的日志告诉我们:对于一个HTTP请求“GET/aaa.html”,重写过程是:先/aaa.html被重写为/bbb.html;然后rewritten data: /bbb.html,继续执行后面的rewrite指令,进而被重写为/ddd.html,然后rewritterndata: /ddd.html 后面没有重写了(其实此时/ddd.html需要再次重新匹配location的,只是日志没有体现出来,接下来的测试2会体现这点),于是输出/ddd.html的内容。
 
[测试2]使用last标记时:请求aaa.html
将上述location /aaa.html {} 修改成:
location /aaa.html {
      rewrite "^/aaa\.html$"  /bbb.html  last;
     rewrite "^/bbb\.html$" /ddd.html;
}  
 
测试结果:
[iyunv@web108 ~]# curlhttp://localhost:9090/aaa.html
ccchtml file
[iyunv@web108 ~]#
 
Error_log日志:
2011/08/07 22:24:31 [notice] 18569#0: *86 "^/aaa\.html$" matches"/aaa.html", client: 127.0.0.1, server: localhost, request:"GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:24:31 [notice] 18569#0: *86 rewritten data: "/bbb.html",args: "", client: 127.0.0.1, server: localhost, request: "GET/aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:24:31 [notice] 18569#0: *86 "^/bbb\.html$" matches"/bbb.html", client: 127.0.0.1, server: localhost, request:"GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:24:31 [notice] 18569#0: *86 rewritten data: "/ccc.html",args: "", client: 127.0.0.1, server: localhost, request: "GET/aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:24:31 [info] 18569#0: *86client 127.0.0.1 closed keepalive connection
 
不知道读者看到GET /aaa.html显示的结果“ccc html file”会不会惊讶:“为什么结果不是bbb html file”。下面解释下整个过程:首先/aaa.html匹配了location /aaa.html {},于是执行rewrite "^/aaa\.html$" /bbb.html last,把/aaa.html重写为/bbb.html,同时由于last flag的使用,后面的rewrite指令(指的是rewrite "^/bbb\.html$" /ddd.html)不会被执行。似乎此时应该输出“bbb html file”才对,但是我们看看nginx官方解释:“last - completes processingof rewrite directives, after which searches for corresponding URI and location”意思是说last不再匹配后面的rewrite指令,但是紧接着需要对重写后的URI重新匹配location。让我们再看看官方的“If the directives of this module are given at the server level, thenthey are carried out before the location of the request is determined. If inthat selected location there are further rewrite directives, then they also arecarried out. If the URI changed as aresult of the execution of directives inside location, then location is againdetermined for the new URI.This cycle can be repeated up to 10 times,after which Nginx returns a 500 error.”因此,重新匹配的时候,匹配到了新的location/bbb.html {},执行“rewrite "^/bbb\.html$" /ccc.html”,最后的内容是“ccchtml file”。
 
[测试3]使用break标记时:请求aaa.html
将上述location /aaa.html {} 修改成使用break标记:
location /aaa.html {
     rewrite "^/aaa\.html$" /bbb.html  break;
     rewrite "^/bbb\.html$" /ddd.html;
}  
测试结果:
[iyunv@web108 ~]# curlhttp://localhost:9090/aaa.html
bbb html file
[iyunv@web108 ~]#
 
日志结果:
2011/08/07 22:37:49 [notice] 21069#0: *89 "^/aaa\.html$" matches"/aaa.html", client: 127.0.0.1, server: localhost, request:"GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:37:49 [notice] 21069#0: *89 rewritten data: "/bbb.html",args: "", client: 127.0.0.1, server: localhost, request: "GET/aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/07 22:37:49 [info] 21069#0: *89client 127.0.0.1 closed keepalive connection
 
我想这个结果不用多做解释了,充分体现了break和last的区别:“last - completes processingof rewrite directives, after which searches for corresponding URI and location”和“break - completes processingof rewrite directives”。Break和last都能阻止继续执行后面的rewrite指令,但是last如果在location下用的话,对于重写后的URI会重新匹配location,但是break则不会重新匹配location。简单的说,break终止的力度比last更加彻底(为了记忆的方便,我们可以把重新后的URI重新匹配location理解为“URI匹配location的循环语句的下一次迭代”,高级程序设计里面break一般用做退出循环,所以break不仅终止继续执行rewrite,而且退出URI重新匹配location的循环迭代)。
 
 
 
 
Nginx关于Rewrite的迭代第二篇
例题1
配置:
error_log logs/error.log info;
 
server {
       listen       9090;
       server_name  localhost;
       root html;
       rewrite_log on;
 
       rewrite "^/aaa\.html$" /bbb.html;
 
       location  /ccc.html {
           rewrite "^/ccc\.html$" /eee.html;
       }
 
       location  /bbb.html {
           rewrite "^/bbb\.html$" /ccc.html;
           rewrite "^/ccc\.html$" /ddd.html;
       }  
}   
结果:
[iyunv@web108 ~]# curlhttp://localhost:9090/aaa.html
ddd html file
[iyunv@web108 ~]#
 
日志:
2011/08/08 10:05:41 [notice] 31592#0: *90 "^/aaa\.html$" matches"/aaa.html", client: 127.0.0.1, server: localhost, request:"GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:05:41 [notice] 31592#0: *90 rewritten data: "/bbb.html",args: "", client: 127.0.0.1, server: localhost, request: "GET/aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:05:41 [notice] 31592#0: *90 "^/bbb\.html$" matches"/bbb.html", client: 127.0.0.1, server: localhost, request:"GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:05:41 [notice] 31592#0: *90 rewritten data: "/ccc.html",args: "", client: 127.0.0.1, server: localhost, request: "GET/aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:05:41 [notice] 31592#0: *90"^/ccc\.html$" matches"/ccc.html", client: 127.0.0.1, server: localhost, request:"GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:05:41 [notice] 31592#0: *90 rewritten data: "/ddd.html",args: "", client: 127.0.0.1, server: localhost, request: "GET/aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:05:41 [notice] 31592#0: *90"^/aaa\.html$" does not match"/ddd.html", client: 127.0.0.1, server: localhost, request:"GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:05:41 [info] 31592#0: *90client 127.0.0.1 closed keepalive connection
 
解释:
GET /aaa.html请求,首先执行server级的rewrite指令,被重写为/bbb.html,然后匹配到location/bbb.html {},接着执行location级的rewrite指令,先重写为/ccc.html,再重写为/ddd.html;由于URI被location级的rewrite指令重写了,因此需要重新进行location的匹配,相当于重写后的URI被当做一个新的请求,会重新执行server级的rewrite,然后重新匹配location,日志“2011/08/08 10:05:41 [notice] 31592#0: *90 "^/aaa\.html$" doesnot match "/ddd.html", client: 127.0.0.1, server: localhost,request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"”体现了重新匹配location的流程。
 
例题2
配置:
error_log logs/error.log info;
 
server {
       listen       9090;
       server_name  localhost;
       root html;
       rewrite_log on;
 
       rewrite "^/aaa\.html$" /bbb.html;
                   rewrite"^/ccc\.html$"  /ddd.html;
       
       location  /bbb.html {
           rewrite "^/bbb\.html$" /ccc.html;
       }  
                   location  /ddd.html {
            rewrite "^/ddd\.html$" /eee.html;
       }
}   
结果:
[iyunv@web108 ~]# curlhttp://localhost:9090/aaa.html
eee html file
[iyunv@web108 ~]#
 
日志:
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/aaa\.html$" matches "/aaa.html",client: 127.0.0.1, server: localhost, request: "GET /aaa.htmlHTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 rewritten data: "/bbb.html",args: "", client: 127.0.0.1, server: localhost, request: "GET/aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/ccc\.html$"does not match "/bbb.html", client: 127.0.0.1, server:localhost, request: "GET /aaa.html HTTP/1.1", host:"localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/bbb\.html$" matches"/bbb.html", client: 127.0.0.1, server: localhost, request:"GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91rewritten data: "/ccc.html",args: "", client: 127.0.0.1, server: localhost, request: "GET/aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/aaa\.html$"does not match "/ccc.html", client: 127.0.0.1, server:localhost, request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/ccc\.html$"matches "/ccc.html", client: 127.0.0.1, server: localhost,request: "GET /aaa.html HTTP/1.1", host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91rewritten data: "/ddd.html", args: "", client:127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1",host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91"^/ddd\.html$" matches "/ddd.html", client:127.0.0.1, server: localhost, request: "GET /aaa.html HTTP/1.1",host: "localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 rewrittendata: "/eee.html", args: "", client: 127.0.0.1,server: localhost, request: "GET /aaa.html HTTP/1.1", host:"localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/aaa\.html$"does not match "/eee.html", client: 127.0.0.1, server:localhost, request: "GET /aaa.html HTTP/1.1", host:"localhost:9090"
2011/08/08 10:21:00 [notice] 2218#0: *91 "^/ccc\.html$"does not match "/eee.html", client: 127.0.0.1, server:localhost, request: "GET /aaa.html HTTP/1.1", host:"localhost:9090"
2011/08/08 10:21:00 [info] 2218#0: *91client 127.0.0.1 closed keepalive connection
 
解释:
第一次迭代location匹配
GET /aaa.html,首先执行server级的重写,“rewrite"^/aaa\.html$"  /bbb.html”把/aaa.html重写为/bbb.html,但/bbb.html没匹配上“rewrite"^/ccc\.html$"  /ddd.html”,最终保留/bbb.html;接着,匹配location/bbb.html {},执行location级的rewrite指令,把/bbb.html重写为/ccc.html,由于URI被location级rewrite重写,因此需要重新迭代location匹配。
 
第二次迭代location匹配
对于第一次迭代结果/ccc.html,首先依然是执行server级的rewrite指令,“rewrite "^/aaa\.html$" /bbb.html;”跟/ccc.html不匹配,但“rewrite "^/ccc\.html$" /ddd.html;”把/ccc.html重写为/ddd.html;server级rewrite执行完后,接着location匹配,/ddd.html匹配到location /ddd.html {},执行location级的rewrite指令,把/ddd.html重写为/eee.html。同样由于URI被location级的rewrite指令重写,于是需要重新迭代location匹配。
 
第三次迭代location匹配
对于第二次迭代结果/eee.html,首先依然执行server级的rewrite指令,“rewrite "^/aaa\.html$" /bbb.html;”和“rewrite "^/ccc\.html$" /ddd.html;”,只不过它们都没匹配上/eee.html,接着/eee.html进行location匹配,也没有,最终结果是/eee.html,返回“eee html file”页面。
 
 
最后说明下,如果把上述配置修改成server级rewrite和location的编辑顺序调整:
server {
        listen       9090;
        server_name  localhost;
        root html;
        rewrite_log on;

              
        location  /bbb.html {
            rewrite "^/bbb\.html$" /ccc.html;
        }  
        location  /ddd.html {
            rewrite "^/ddd\.html$" /eee.html;
        }
 
        rewrite "^/aaa\.html$"  /bbb.html;
        rewrite "^/ccc\.html$"  /ddd.html;
}
结果是不会受影响的,也就是说location匹配迭代总是先执行server级rewrite,再进行location匹配,再执行location级的rewrite,如果URI因location级rewrite指令重写,则需要进行下一次迭代。但总的迭代次数不超过10次,否则nginx报500错误。
 
简单伪代码描述下rewrite执行过程:
 
boolean match_finish = false;
int match_count = 0;
while(!match_finish && match_count < 10) {
        match_count ++;
    (1)按编辑顺序执行server级的rewrite指令;
    (2)按重写后的URI匹配location;
    (3)
        String uri_before_location = uri;
        按编辑顺序执行location级的rewrite指令;
        String uri_after_location = rewrite(uri);
        if(uri_before_location != uri_after_location) {
            match_finish = false;           
        } else {
            match_finish = true;
        }
        if(location rewrite has last flag) {
            continue;//表示不执行后面的rewrite,直接进入下一次迭代
        }
        if(location rewrite has break flag) {
            break;//表示不执行后面的rewrite,并退出循环迭代
        }
}
if(match_count <= 10) {
    return HTTP_200;
} else {
    return HTTP_500;
}

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-319841-1-1.html 上篇帖子: 关于 Nginx+Tomcat+Memcached做负载均衡加共享session 下篇帖子: Ubuntu10下Nginx-0.8.54安装(转)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表