nginx:直接从位置发送到另一个命名位置

nginx:直接从位置发送到另一个命名位置

在我的 nginx 1.12.2conf 文件中,我有:

upstream app {
  server unix:/tmp/app.sock  fail_timeout=0;
}

server {
  listen 443 deferred;

  root /some/dir;

  try_files $uri @app;

  # If the request is for the naked domain, just serve the index bundle
  # without going through Rails
  #
  location = / {
    try_files /index.html =404;
  }

  # If the request if for the /api prefix, go directly to Rails.
  # This location block is not strictly required, but it could be a handy
  # customization hook for extra headers and settings.
  #
  location /api/ {
    # Extra conf!
    try_files @app;
  }

  # The location directory allows very creative configurations.
  # http://nginx.org/en/docs/http/ngx_http_core_module.html#location
  #
  # This is just a named location to be used in try_files.
  location @app {
    proxy_pass_request_headers on;
    proxy_set_header ...
    proxy_pass http://app;
  }
}

这并不正确,因为它只有一个参数:

  location /api/ {
    # Extra conf!
    try_files @app;
  }

...但它很好地传达了我想要实现的目标。我想我可以try_files通过在最后一个参数之前添加一个不存在的文件来开始工作。

这是try_files唯一的方法吗,还是还有其他更惯用的指令?

答案1

您的方案将不起作用。当nginx确定最后一个location块来处理请求时,它将使用范围内的“设置和标头”,这些设置和标头可能是从周围块继承的,但将不会包括来自兄弟块的任何“额外标题和设置”——无论采用何种过程来查找最终location块。请参阅这个文件了解更多信息。

如果您有适用于多个位置的通用语句,则可以将它们卸载到单独的文件中,并在必要时包含它们。例如:

location / {
    try_files $uri @app;
}
location /api/ {
    # Extra conf!
    include my/proxy/conf;
}
location @app {
    include my/proxy/conf;
}

这个文件了解更多信息。

答案2

我们的 Nginx 配置相当复杂,我们希望使用命名位置来让事情变得更加简单干燥。由于这个要求,我们必须做你想做的事情。

可悲的是:我们找不到任何直接的方法来做到这一点,但好消息是你可以使用一个try_files不影响性能的指令来欺骗 Nginx,将第一个参数指向/dev/null

try_files /dev/null @the_named_location;

这绝对是一种解决方法,通常我会烧毁任何包含此 hack 的 pull request,并仔细向开发人员解释不遵守标准的可怕之处,等等……但在你被说服并接受这个可悲的现实后,它实际上会让你的配置看起来好多了。真的好多了!

我们认为这种“黑客行为”是“可以接受的”,因为事实证明,就整体代码质量而言,它带来了巨大的改进。

(但有时我的眼睛仍然会疼痛……)

答案3

几个第三方模块指令允许直接跳转到指定位置,例如ngx.exec来自 lua-nginx-module 或echo_exec来自 echo-nginx-module(两个模块都与OpenResty包)。但是使用“vanilla”nginx,唯一的方法是使用try_files指令技巧。不幸的是,@VictorSchröder假设用作/dev/null参数try_files将消除由于系统stat调用错误而导致的性能损失,在这种情况下/webroot/dev/null将被stat编辑的是文件。每个人都可以使用下列的指令。因此,它与常用的解决方案没有区别,例如

try_files /nonexistent @app;

但是,可以传递一个空字符串作为参数try_files

try_files "" @app;

这不会消除stat系统调用。但是,在这种情况下,webroot 目录将被stat执行并检查为文件系统上现有的物理文件,但它很可能不是(好吧,至少除非您将服务器或位置根指向这样的文件,但您为什么要做这么疯狂的事情?)根据我的假设,现代内核应该stat比不存在的文件系统对象更有效地缓存系统调用结果。这只是一个假设,所以如果有人读到这篇文章,更了解内核在这种情况下的行为,我很乐意听取任何澄清。

我认为最后一部分需要澄清一下。为什么该try_files指令检查 Web 根目录是物理文件而不是目录?这是因为参数""不是以斜杠结尾的。以斜杠结尾的参数会将try_files其测试为目录,而不以斜杠结尾的参数会将try_files其测试为文件。也就是说,如果位置根目录指向现有目录,则try_files "/" ...第一个检查将始终成功(并且显式或隐式index指令将在下一个请求处理阶段执行索引文件搜索的工作,假设该位置具有静态内容处理程序),try_files "" ...在这种情况下,第一个检查将始终失败。

服务器 webroot 也可能是一个不存在的目录,例如当 nginx 仅用于反向代理时。与许多其他 nginx 指令一样root,如果该指令未从之前的配置级别继承,/html则相对于 nginx 前缀具有默认值。该前缀是在构建时指定的,可以使用命令进行检查nginx -V(请参阅--prefix=...configure 参数)。例如,如果此前缀等于/etc/nginx(常见情况),则默认服务器 webroot 将为,/etc/nginx/html而对于大多数环境来说,这不太可能是现有目录。

try_files "" ...但我还是更喜欢try_files /nonexistent ...。如果nonexistent文件以任何方式出现在 webroot 目录中(想想某种破坏性的黑客攻击),第一个将继续工作,而第二个将不会。

try_files "" ...当使用指令指定 webroot 目录时,可能会有构造用例alias,尤其是对于精确或正则表达式匹配的位置。但是我看不出有任何可能的用例将它放在使用root一个的位置内,所以我认为 nginx 开发团队可能会考虑更新try_files模块源代码完全消除stat这种使用情况下的额外调用。这将使“无条件”位置跳转成为可能,而不会产生任何额外的性能损失,同时保持与任何现有配置的兼容性。

相关内容