NginX OpenResty的内建及扩展模块的phase先后执行次序

kevin.Zhu 发布于:2022-3-30 16:46 分类:文摘  有 11 人浏览,获得评论 0 条  

https://gist.github.com/fannheyward/39495ede697f423c3d7a

NginX/NginX OpenResty的内建及扩展模块的phase先后执行次序:

参考: http://wiki.nginx.org/Phases
     http://blog.sina.com.cn/openresty

需要关注的phase有:
-1:http config:
   在/usr/local/openresty/nginx/conf/nginx.conf的http段落有可以载入lua的共用函数, 比如:
   init_by_lua_file   /var/workspace/www/violation_nginx/lf;
0.server selection:       server(listen, server_name)
  匹配listen端口和server_name
1.server rewrite:         set, rewrite, return, set_by_lua
2.server rewrite tail:    more_set_input_headers, rewrite_by_lua
  即使是server阶段的rewrite也会重新从server selection再进来一次
3.server access:          allow, deny
4.server access tail:     access_by_lua
  server access就是一次过不会像rewrite一样重新进来
5.server try files:       try_files
6.location selection:     location
  选择处理匹配的url, 该phase无第三方模块插入
  prefix strings之间遵循"最长子串匹配原则", prefix的意思是从url首部匹配起
  regular expressions之间遵循"先定义优先匹配原则", regex是匹配整个url的规则
  优先次序: stop regex prefix string(^~)>regular expression>prefix string

  location = {exact_url}
  location ^~ {prefix_string_if_any}
  location ~ {case_sensitive_regular_expression}
  location ~* {case_insensitive_regular_expression}
  location {prefix_string_if_no_RE_match}      #这个不太严格, /shanghaikkk 的请求也会进入 /shanghai 的location代码内

  if (-f和!-f)     判断是否存在location或文件
  if (-d和!-d)     判断是否存在location或目录
  if (-e和!-e)     判断是否存在location或文件或目录
  if (-x和!-x)     判断文件是否可执行
  if ($a = $b 和 $a != $b)
  if ($a ~ $b 和 $a !~ $b)
  if ($a ~* $b 和 $a !~* $b)
  location /
  {
        #"location if"导致其所在location比"server rewrite"都先执行, 破坏了官方文档里server比location早的次序(这才是if is evil的真正原因), 所以改为try_files
        #if (!-e $request_filename){
        #            rewrite ^/(.*)$ /index.php/$1 last;
        #}
        try_files $uri $uri/ /index.php$uri;

        index           index.php index.html index.htm;      # rewrite阶段在index的content阶段前面
  }
  其中ngx.var.request_filename==ngx.var.document_root..ngx.var.uri, 奇怪的是if -e $request_filename首先检查的是location且能匹配上

  调试location匹配分支, 不要用echo, content_by_lua因为会晚于rewrite, return, access, try files等命令, 用rewrite_by_lua或直接用return:
  return 200 'hello';
  如果要用echo等可加break(但只屏蔽同段落之后部份, 但若在server段,http段定义access是继承到每个location rewrite之后就执行, 比echo提前):
  echo 'jack'; break;
7.location rewrite:       set, rewrite, return, set_by_lua
  set, set_by_lua同处location rewrite阶段且可以混合运行
  rewrite直接发起内部重定向, 改写$document_uri(ngx.var.document_uri)等同$uri(ngx.var.uri), 不动原始请求:$request_uri(ngx.var.request_uri), 这个含参数, 前两个不含GET参数,
  并跳回location selection阶段重新执行, 跳转后$request_uri仍不改变, 所以php里面的route用的是原始请求比如"/help_lua/write_file"而不是"/index.php/help_lua/write_file"
  实现http里的location redirect应该用openresty的第9 phase里的more_set_headers或: return 301 https://mydomain/newurl; 或: ngx.redirect
  而proxy_pass是进行代理请求, 直接返回内容,
  而proxy_redirect的意思则是把response header里的location进行替换, 一般跟在proxy_pass后面.
  根据系统的环境变量设置参数:
  set_by_lua $ENVIROMENT 'return os.getenv("ENVIROMENT")';
  但只有proxy_pass等少数命令支持变量, 像memcached_pass等命令不支持(memcache服务器ip还可以通过设本地host实现, port则无法根据环境变化: memcached_pass memcache_server:11215;)
8.location rewrite tail:  more_set_input_headers, rewrite_by_lua
  more_set_input_headers可以改写header, rewrite_by_lua在location rewrite tail阶段执行代码
  rewrite_by_lua内ngx.req.set_uri("/foo", true);相当于location rewrite步骤的rewrite ^ /foo last; 注意rewrite_by_lua内ngx.req.set_uri实际是必须带第二参数的, 也就是rewrite last
  rewrite_by_lua内ngx.req.set_uri_args("a=3");ngx.req.set_uri("/foo", true);相当于rewrite ^ /foo?a=3? last;
  rewrite_by_lua内return ngx.exec('/foo?a=3');相当于rewrite ^ /foo?a=3? last;  由于ngx.exec不会主动返回, 所以一般前面都加"return "
  rewrite_by_lua内ngx.req.set_uri(ngx.re.sub(ngx.var.uri, "^/test/(.*)", "$1", "o"), false);相当于rewrite ^/test/(.*) /$1 break;
  为避免死循环(11次rewrite后系统报500错误)可以加上ngx.req.set_header("city_2_api", "1")来设标志, 加上ngx.req.get_headers()["city_2_api"]来取标志,
  当然也可直接set $city_2_api 0;然后ngx.var.city_2_api=1;
  跳转后nginx里还要用$request_uri的地方可以改用自己set的$updated_request_uri,
  而fastcgi_pass到php-fpm的location内也可以加上fastcgi_param REQUEST_URI $updated_request_uri; 当然server内第一步就要set $updated_request_uri $request_uri;
9.access:                 allow, deny
  deny相当于ngx.exit(403), allow/deny后参数可以是IP, IP/mask(16,24等), all
10.access tail:            access_by_lua
  access_by_lua在access tail阶段执行代码
  access_by_lua不会运行于ngx.location.capture内
  access_by_lua内有ngx.say输出则content阶段不会被执行到
11.try files:              try_files
  try_files最后一个参数之前的参数是正常的在检查file/folder存在, 存在则改写$uri内置参数(然后交给下面content输出模块或index模块或static模块处理),
  最后一个参数不检查文件存在而直接internal redirect
12.content:                echo, proxy_pass, fastcgi_pass, content_by_lua
  多个echo和一个content_by_lua同处content phase,
  但ngx_proxy, ngx_echo, ngx_lua多个content模块不是混合运行而是独立运行的,
  排在后面的模块覆盖content的值, ngx_echo模块启动以第一个echo为准.
  对"/"结尾的location且没有上面content输出模块启动才触发index, 类似try_files检查多个文件名是否存在而不是直接重定向, 存在则内部重定向, 都不存在则执行autoindex模块, 如果最后参数是"/..."则直接internel redirect
  对"/"结尾的location且没有上面content模块启动, autoindex=on, 则找不到index时list目录, 如果off(默认)则自动ngx.exit(404); 未找到
  ngx_index模块找到文件内部重定向后将由ngx_static来输出这个找到的文件, 既没有content输出,也没有nginx_index,则ngx_static把url location映射位置的文件输出

  跟fastcgi_pass一起的还有fastcgi_index, fastcgi_param, include指令:
  "include fastcgi_params;", include可以在各个级别, 这里就是包含"/etc/nginx/"(include的根目录, openresty是/usr/local/openresty/nginx, 用"nginx -V"看prefix)内的fastcgi_params文件, 里面有很多fastcgi_param定义指令
  ngx.location.capture并不重新指定fastcgi_params里用到的$fastcgi_script_name, $request_uri, $remote_addr这几个nginx变量, 而是直接继承调用它的那个request里的环境
13.output header filter:   more_set_headers
  输出header, 如more_set_headers 'Content-Type: text/html'; 配置return 200 'hello'; 这里无法用echo, 因return在rewrite phase比echo的content phase早
  9和10默认NginX未安装, NginX OpenResty带有
14.output filter:         echo_before_body, echo_after_body, body_filter_by_lua
  最后的output filter phase用来修改content的值
  body_filter_by_lua如果改变内容长度得把content_length header先移除:
  header_filter_by_lua 'ngx.header.content_length = nil';
  body_filter_by_lua 'ngx.arg[1] = string.upper(ngx.arg[1]).."end of the file"';