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

[经验分享] nginx中rewrite模块break和last原理详解

[复制链接]

尚未签到

发表于 2016-12-28 06:25:19 | 显示全部楼层 |阅读模式
  


在使用nginx重写(即rewrite)机制时,大家一般会用到last和break,关于这两个指令的作用,网友问的挺多,网上的讨论也挺多,这里做个总结:

网友的给力解释:

last:

    重新将rewrite后的地址在server标签中执行

break:

    将rewrite后的地址在当前location标签中执行

nginx官方解释:
last:
    stops processing the current set of ngx_http_rewrite_module directives followed by a search for a new location matching     
    the changed URI;
break:
    stops processing the current set of ngx_http_rewrite_module directives;


其实网友的解释更容易懂一些,nginx官方的解释则是从偏重实现的角度来说的,说到这里,许多人可能还是对这两个指令的使用不是太自信,感觉心里没底,说实话我当初也是这么感觉的,那么就让我们打破沙锅问到底,看看代码到底是怎么实现的,毕竟,”代码面前无秘密“。


为了方便我们的讨论我们做出以下的假想配置:


[cpp] view plaincopy







  • location /download/ {   
  •     rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 break;  
  •     rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra  break;  
  •     return  403;  
  • }  





在分析之前,看官们需要熟悉nginx各个phase handler的处理,以及nginx变量的基本原理,不熟悉的同学看起来可能会有点难度,那么这里给出了相关的连接,方面不熟悉的同学学习。前两个是关于handler的,后一个是关于变量的:

http://simohayha.iyunv.com/blog/670326

http://simohayha.iyunv.com/blog/679314

http://blog.lifeibo.com/?p=346




现在进入正题:

在函数ngx_http_rewrite中:


[cpp] view plaincopy







  • if (cf->args->nelts == 4) {  
  •         if (ngx_strcmp(value[3].data, "last") == 0) {  
  •             last = 1;  
  •   
  •         } else if (ngx_strcmp(value[3].data, "break") == 0) {  
  •             regex->break_cycle = 1;  
  •             last = 1;  
  •   
  •         }    
  •         ...// 其余处理省略  
  • }  





重点在于last = 1的处理,在稍后:


[cpp] view plaincopy







  • if (last) {  
  •     code = ngx_http_script_add_code(lcf->codes, sizeof(uintptr_t), ®ex);  
  •     if (code == NULL) {  
  •         return NGX_CONF_ERROR;  
  •     }  
  •   
  •     *code = NULL;  
  • }  





lcf->codes是个数组里面保存了当前各个rewrite执行对应的相关操作(即各种handler)和数据,这里的操作是在这个数组中添加一个null,这个null的意义重大,在rewrite实际执行时,如ngx_http_rewrite_handler的调用,就会对事先放置在这个数组里的handler进行处理:


[cpp] view plaincopy







  • e->ip = rlcf->codes->elts;  
  • ...  
  • // 在这个while循环中,上面的那个null,就会终止rewrite一系列操作的执行  
  • // 可以看到,“last”和“break”在这点上作用是相同的,当前codes数组中有剩余的  
  • // rewrite指令,那么由于这里的null的存在,也就跳过不管了。  
  • while (*(uintptr_t *) e->ip) {  
  •     code = *(ngx_http_script_code_pt *) e->ip;  
  •     code(e);  
  • }  





比如在开始的配置里面,我们写成:


[cpp] view plaincopy





  • location /download/ {  
  •     rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3;  
  •     rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra;  
  •     return  403;  
  • }  





即不写last和break,那么流程就是依次执行这些rewrite,直到最后以403结束这次请求,这种情况下codes数组中的handler都得以执行了,而由于

last和break的出现,处理可能在中间的某个位置终止,后面的rewrite,就不会执行了。




在rewrite阶段的处理结束之后,则会转到find config阶段,这个阶段本来是在rewrite阶段之前的,这样的过程也刻画了rewrite的基本流程,url经过rewrite阶段被改变了,而一个请求处理的关键步骤之一就是要确定对应的server conf和location conf,而find config的作用恰恰就是如此,重写之后url可以看做是一个新的请求,所以这些关键步骤需要走一遍就是理所当然了。




另一个问题,在ngx_http_rewrite函数中break_cycle的设置,也就是在出现break的时候,这个变量会被置1,而这个变量的设置,最终会导致r->uri_changed被置为0,那么它的直接影响可以在下面的地方看到:


[cpp] view plaincopy







  • // 这个函数之所以名为“post”,意思就是为rewrite处理做一些善后工作  
  • ngx_http_core_post_rewrite_phase  
  • {     
  •     // 在通常情况下,即r->uri_changed > 0,r->phase_handler会设置为ph->next,  
  •     // 而这个ph->next,在开始初始化phase的时候,已经设置为ph->next = find_config_index,  
  •     // 所以在非break或者last情况下,之后的phase就是所谓find config阶段了,而这里却是  
  •     // r->phase_handler++,意味着将会执行接下来的处理,不会再去走find config的过程了  
  •     if (!r->uri_changed) {  
  •         r->phase_handler++;  
  •         return NGX_AGAIN;  
  •     }  
  •     ... // 此处省略其他处理部分  
  • }  





关于r->uri_changed被置为0的操作,可以参考:

ngx_http_script_regex_start_code和ngx_http_script_break_code







所以这里概括下:

last其实就相当于一个新的url,对nginx进行了一次请求,需要走一遍大多数的处理过程,最重要的是会做一次find config,提供了一个可以转到其他location的配置中处理的机会,而break则是在一个请求处理过程中将原来的url(包括uri和args)改写之后,在继续进行后面的处理,这个重写之后的请求始终都是在同一个location中处理。

运维网声明 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-320248-1-1.html 上篇帖子: Nginx 配置日志打印--HTTP报文 下篇帖子: 解剖Nginx·模块开发篇(6)配置文件config入门
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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