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

tom*_*ave 14 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  location /api/ {
    # Extra conf!
    try_files @app;
  }
Run Code Online (Sandbox Code Playgroud)

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

try_files唯一的方法,还是有另一个更惯用的指令?

Vic*_*der 30

我们有一个非常复杂的 Nginx 配置,我们想使用命名位置来让事情更干一些。由于这个要求,我们必须完全按照您的要求去做。

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

try_files /dev/null @the_named_location;
Run Code Online (Sandbox Code Playgroud)

这绝对是一种解决方法,通常我会在火焰中烧毁任何包含此 hack 的拉取请求,并仔细向开发人员解释不遵守标准的可怕之处,等等......看起来好多了。真的更好看!

我们认为这种“hack”是“可以接受的”,因为事实证明它在总体代码质量方面带来了很大的改进。

(但它有时仍然伤害我的眼睛......)

  • 不幸的是@intelfx、`try_files` 和`if` 不能很好地配合使用。主要问题是 `if` 的行为不是我们大多数人所期望的(它应该看起来更像是另一个位置块,而**不是**作为真正的逻辑分支)。广为人知的“If is Evil”博客文章 (https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/) 有助于理解原因。您可以随时尝试并使用大量测试来验证您的配置(无论如何您都应该这样做),但我不建议您违背 Nginx 本身的建议。 (2认同)
  • 这太棒了。请注意,它也适用于 _variable_ 命名位置!例如,将其与 `map $http_cookie $upstream` 结合以将 `$upstream` 设置为 `@named_location` 允许您根据 cookie 值跳转位置。 (2认同)

Iva*_*sky 15

一些第三方模块指令允许直接跳转到指定位置,例如ngx.exec从 lua-nginx-module 或echo_exececho-nginx-module(这两个模块都与OpenResty包捆绑在一起)。然而,使用“vanilla”nginx 的唯一方法是使用try_files指令技巧。不幸的是,使用@VictorSchr\xc3\xb6der作为参数的假设将消除由于系统调用错误而导致的性能损失,在这种情况下将被编辑的文件。每个人都可以按照以下说明进行检查并亲自查看。所以它与常用的解决方案没有区别,例如/dev/nulltry_filesstat/webroot/dev/nullstat

\n
try_files /nonexistent @app;\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
try_files "" @app;\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n

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

\n

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

\n

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

\n


Ric*_*ith 10

你的计划不会奏效。当nginx确定最后一个location块来处理请求时,它将使用范围内的“设置和标头”,这些设置和标头可以从周围的块继承,但不会包括来自同级块的任何“额外标头和设置” -无论寻找最终location区块的过程如何。请参阅此文档了解更多信息。

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

location / {
    try_files $uri @app;
}
location /api/ {
    # Extra conf!
    include my/proxy/conf;
}
location @app {
    include my/proxy/conf;
}
Run Code Online (Sandbox Code Playgroud)

请参阅此文档了解更多信息。