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

[经验分享] Nginx 配置指令的执行顺序(五)content & 输出过滤器 (转载)

[复制链接]

尚未签到

发表于 2016-12-28 09:47:09 | 显示全部楼层 |阅读模式
  转载自 http://blog.sina.com.cn/openresty
  Nginx 的 content 阶段是所有请求处理阶段中最为重要的一个,因为运行在这个阶段的配置指令一般都肩负着生成“内容”(content)并输出 HTTP 响应的使命。正因为其重要性,这个阶段的配置指令也异常丰富,例如前面我们一直在示例中广泛使用的 echo 指令,在 Nginx 变量漫谈(二) 中接触到的 echo_exec 指令, Nginx 变量漫谈(三) 中接触到的 proxy_pass 指令,Nginx 变量漫谈(五) 中介绍过的 echo_location 指令,以及 Nginx 变量漫谈(七) 中介绍过的 content_by_lua 指令,都运行在这个阶段。
  content 阶段属于一个比较靠后的处理阶段,运行在先前介绍过的 rewrite 和 access 这两个阶段之后。当和 rewrite、access 阶段的指令一起使用时,这个阶段的指令总是最后运行,例如:
  location /test {
  # rewrite phase
  set $age 1;
  rewrite_by_lua "ngx.var.age = ngx.var.age + 1";
  # access phase
  deny 10.32.168.49;
  access_by_lua "ngx.var.age = ngx.var.age * 3";
  # content phase
  echo "age = $age";
  }
  这个例子中各个配置指令的执行顺序便是它们的书写顺序。测试结果完全符合预期:
  $ curl 'http://localhost:8080/test'
  age = 6
  即使改变它们的书写顺序,也不会影响到执行顺序。其中,set 指令来自 ngx_rewrite 模块,运行于 rewrite 阶段;而 rewrite_by_lua 指令来自 ngx_lua 模块,运行于 rewrite 阶段的末尾;接下来,deny 指令来自 ngx_access 模块,运行于 access 阶段;再下来,access_by_lua 指令同样来自 ngx_lua 模块,运行于 access 阶段的末尾;最后,我们的老朋友 echo 指令则来自 ngx_echo 模块,运行在 content 阶段。
  这个例子展示了通过同时使用多个处理阶段的配置指令来实现多个模块协同工作的效果。在这个过程中,Nginx 变量则经常扮演着在指令间乃至模块间传递(小份)数据的角色。这些配置指令的执行顺序,也强烈地受到请求处理阶段的影响。
  进一步地,在 rewrite 和 access 这两个阶段,多个模块的配置指令可以同时使用,譬如上例中的 set 指令和 rewrite_by_lua 指令同处 rewrite 阶段,而 deny 指令和 access_by_lua 指令则同处 access 阶段。但不幸的是,这通常不适用于 content 阶段。
  绝大多数 Nginx 模块在向 content 阶段注册配置指令时,本质上是在当前的 location 配置块中注册所谓的“内容处理程序”(content handler)。每一个 location 只能有一个“内容处理程序”,因此,当在 location 中同时使用多个模块的 content 阶段指令时,只有其中一个模块能成功注册“内容处理程序”。考虑下面这个有问题的例子:
  ? location /test {
  ?     echo hello;
  ?     content_by_lua 'ngx.say("world")';
  ? }
  这里,ngx_echo 模块的 echo 指令和 ngx_lua 模块的 content_by_lua 指令同处 content 阶段,于是只有其中一个模块能注册和运行这个 location 的“内容处理程序”:
  $ curl 'http://localhost:8080/test'
  world
  实际运行结果表明,写在后面的 content_by_lua 指令反而胜出了,而 echo 指令则完全没有运行。具体哪一个模块的指令会胜出是不确定的,例如把上例中的 echo 语句和 content_by_lua 语句交换顺序,则输出就会变成 hello,即 ngx_echo 模块胜出。所以我们应当避免在同一个 location 中使用多个模块的 content 阶段指令。
  将上例中的 content_by_lua 指令替换为 echo 指令就可以如愿了:
  location /test {
  echo hello;
  echo world;
  }
  测试结果证明了这一点:
  $ curl 'http://localhost:8080/test'
  hello
  world
  这里使用多条 echo 指令是没问题的,因为它们同属 ngx_echo 模块,而且 ngx_echo 模块规定和实现了它们之间的执行顺序。值得一提的是,并非所有模块的指令都支持在同一个 location 中被使用多次,例如 content_by_lua 就只能使用一次,所以下面这个例子是错误的:
  ? location /test {
  ?     content_by_lua 'ngx.say("hello")';
  ?     content_by_lua 'ngx.say("world")';
  ? }
  这个配置在 Nginx 启动时就会报错:
  [emerg] "content_by_lua" directive is duplicate ...
  正确的写法应当是:
  location /test {
  content_by_lua 'ngx.say("hello") ngx.say("world")';
  }
  即在 content_by_lua 内联的 Lua 代码中调用两次 ngx.say 函数,而不是在当前 location 中使用两次 content_by_lua 指令。
  类似地,ngx_proxy 模块的 proxy_pass 指令和 echo 指令也不能同时用在一个 location 中,因为它们也同属 content 阶段。不少 Nginx 新手都会犯类似下面这样的错误:
  ? location /test {
  ?     echo "before...";
  ?     proxy_pass http://127.0.0.1:8080/foo;
  ?     echo "after...";
  ? }
  ?
  ? location /foo {
  ?     echo "contents to be proxied";
  ? }
  这个例子表面上是想在 ngx_proxy 模块返回的内容前后,通过 ngx_echo 模块的 echo 指令分别输出字符串 "before..." 和 "after...",但其实只有其中一个模块能在 content 阶段运行。测试结果表明,在这个例子中是 ngx_proxy 模块胜出,而 ngx_echo 模块的 echo 指令根本没有运行:
  $ curl 'http://localhost:8080/test'
  contents to be proxied
  要实现这个例子希望达到的效果,需要改用 ngx_echo 模块提供的 echo_before_body 和 echo_after_body 这两条配置指令:
  location /test {
  echo_before_body "before...";
  proxy_pass http://127.0.0.1:8080/foo;
  echo_after_body "after...";
  }
  location /foo {
  echo "contents to be proxied";
  }
  测试结果表明这一次我们成功了:
  $ curl 'http://localhost:8080/test'
  before...
  contents to be proxied
  after...
  配置指令 echo_before_body 和 echo_after_body 之所以可以和其他模块运行在 content 阶段的指令一起工作,是因为它们运行在 Nginx 的“输出过滤器”中。前面我们在 (一) 中分析 echo 指令产生的“调试日志”时已经知道,Nginx 在输出响应体数据时都会调用“输出过滤器”,所以 ngx_echo 模块才有机会在“输出过滤器”中对 ngx_proxy 模块产生的响应体输出进行修改(即在首尾添加新的内容)。值得一提的是,“输出过滤器”并不属于 (一) 中提到的那 11 个请求处理阶段(毕竟许多阶段都可以通过输出响应体数据来调用“输出过滤器”),但这并不妨碍 echo_before_body 和 echo_after_body 指令在文档中标记下面这一行:
  phase: output filter
  这一行的意思是,当前配置指令运行在“输出过滤器”这个特殊的阶段。

运维网声明 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-320509-1-1.html 上篇帖子: Nginx 0.8.x + PHP 5.2.13(FastCGI)搭建胜过Apache十倍的Web服务器(第6版 下篇帖子: 高性能Web服务器Nginx的配置与部署研究(7)核心模块之主模块的非测试常用指令
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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