nginx反向代理-尝试上游A,然后尝试B,然后再次尝试A


22

我正在尝试将nginx设置为具有大量后端服务器的反向代理。我想按需启动后端(根据传入的第一个请求),因此我有一个控制过程(由HTTP请求控制),该过程根据后端收到的请求来启动后端。

我的问题是配置nginx来做到这一点。这是我到目前为止的内容:

server {
    listen 80;
    server_name $DOMAINS;

    location / {
        # redirect to named location
        #error_page 418 = @backend;
        #return 418; # doesn't work - error_page doesn't work after redirect

        try_files /nonexisting-file @backend;
    }

    location @backend {
        proxy_pass http://$BACKEND-IP;
        error_page 502 @handle_502; # Backend server down? Try to start it
    }

    location @handle_502 { # What to do when the backend server is not up
        # Ping our control server to start the backend
        proxy_pass http://127.0.0.1:82;
        # Look at the status codes returned from control server
        proxy_intercept_errors on;
        # Fallback to error page if control server is down
        error_page 502 /fatal_error.html;
        # Fallback to error page if control server ran into an error
        error_page 503 /fatal_error.html;
        # Control server started backend successfully, retry the backend
        # Let's use HTTP 451 to communicate a successful backend startup
        error_page 451 @backend;
    }

    location = /fatal_error.html {
        # Error page shown when control server is down too
        root /home/nginx/www;
        internal;
    }
}

这行不通-nginx似乎忽略了控制服务器返回的任何状态代码。error_page@handle_502位置中的所有指令均不起作用,并且451代码按原样发送给客户端。

我放弃了尝试为此使用内部Nginx重定向的方法,并尝试修改控制服务器以将307重定向发送到同一位置(以便客户端将重试相同的请求,但现在后端服务器已启动)。然而,尽管控制服务器正在发送“ Location”报头,但是现在nginx愚蠢地用从后端请求尝试获得的状态码覆盖状态码(502)。我最终通过将error_page行更改为“工作”error_page 502 =307 @handle_502;,从而迫使所有控制服务器回复均以307码发送回客户端。这非常骇人且不受欢迎,因为1)无法根据控制服务器的响应来控制nginx接下来应执行的操作(理想情况下,仅当控制服务器报告成功时,我们才想重试后端),以及2)并非所有HTTP客户端支持HTTP重定向(例如curl用户和使用libcurl的应用程序需要显式启用以下重定向)。

使nginx尝试先代理到上游服务器A,然后再代理B,然后再代理A的正确方法是什么(理想情况下,仅当B返回特定的状态码时)?

Answers:


20

关键点:

  • upstream如果对一台服务器执行ping操作会导致另一台服务器出现故障,请不要理会故障转移的块-无法告诉nginx(至少不是FOSS版本)第一台服务器再次启动。nginx的将尝试在服务器上,以第一个请求,但没有后续请求,尽管任何backupweightfail_timeout设置。
  • 必须能够recursive_error_pages实现使用故障转移时error_page和命名的位置。
  • 启用proxy_intercept_errors以处理从上游服务器发送的错误代码。
  • =语法(例如error_page 502 = @handle_502;)需要正确地处理在指定的位置的错误代码。如果=未使用,则nginx将使用上一个块中的错误代码。

原始答案/研究记录如下:


我发现这是一个更好的解决方法,由于不需要客户端重定向,因此这是一项改进:

upstream aba {
    server $BACKEND-IP;
    server 127.0.0.1:82 backup;
    server $BACKEND-IP  backup;
}

...

location / {
    proxy_pass http://aba;
    proxy_next_upstream error http_502;
}

然后,只要让控制服务器返回“成功” 502,并希望后端永远不会返回代码。


更新:nginx一直将upstream块中的第一个条目标记为down,因此它不会在连续的请求中按顺序尝试服务器。我尝试添加weight=1000000000 fail_timeout=1到第一个条目无效。到目前为止,我还没有找到任何不涉及客户端重定向的解决方案。


编辑:我希望知道的一件事-要从error_page处理程序中获取错误状态,请使用以下语法:error_page 502 = @handle_502;-等号将导致nginx从处理程序中获取错误状态。


编辑:我得到它的工作!除了上述error_page修复之外,还需要启用recursive_error_pages


1
对我来说,proxy_next_upstream成功的秘诀是(我的情况没有您的复杂),我只是想让nginx在发生错误时尝试下一个服务器,因此我不得不添加proxy_next_upstream error timeout invalid_header non_idempotent;non_idempotent,因为我主要是转发POST请求)。
菲利普

1

您可以尝试以下操作

upstream backend {
    server a.example.net;
    server b.example.net backup;
}

server {
    listen   80;
    server_name www.example.net;

    proxy_next_upstream error timeout http_502;

    location / {
        proxy_pass http://backend;
        proxy_redirect      off;
        proxy_set_header    Host              $host;
        proxy_set_header    X-Real-IP         $remote_addr;
        proxy_set_header    X-Forwarded-for   $remote_addr;
    }

}

nginx a.example.net在同一请求上一次失败后将不重试。它将向客户端发送尝试连接时遇到的错误b.example.net,除非我也将在控制服务器中实现代理,否则这不会是他们期望的错误。
弗拉基米尔·潘捷列夫2013年

在下一种情况下,您的配置将如何:向上游A返回失败的请求,上游B返回失败的请求,然后我们再次尝试上游A并也失败(502)?
ALex_hha 2013年

上游B是控制服务器。其目的是确保对上游A的下一个请求将成功。目标是尝试上游A,如果失败,则尝试上游B,如果“成功”(使用我们内部的“成功”惯例),请再次尝试上游A。如果我的问题还不够清楚,请告诉我如何改善它。
弗拉基米尔·潘捷列夫2013年

嗯,我们假设上游A关闭了,例如由于某些硬件问题。是什么使上游B?是否能够返回客户端的请求响应?
ALex_hha 2013年

此问题与硬件故障的故障转移无关。此问题与按需启动上游后端有关。如果控制服务器(上游B)无法重新激活后端(上游A),则理想情况下,用户应该收到一条适当的错误消息,但这不是我要解决的问题-问题是让Nginx重试在同一请求内,A再次在B之后。
弗拉基米尔·潘捷列夫2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.