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

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

我正在尝试将 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 返回特定状态代码时)?

答案1

关键点:

  • 如果 ping 一台服务器会使另一台服务器启动,那么不要upstream为故障转移而烦恼 —— 没有办法告诉 nginx(至少不是 FOSS 版本)第一台服务器已再次启动。nginx 会在第一次请求时按顺序尝试服务器,但不会尝试后续请求,无论有任何backupweightfail_timeout设置。
  • 必须在使用和命名位置recursive_error_pages实现故障转移时启用。error_page
  • 启用proxy_intercept_errors处理从上游服务器发送的错误代码。
  • 需要语法(例如)才能正确处理指定位置的错误代码。如果不=使用,nginx 将使用前一个块中的错误代码。error_page 502 = @handle_502;=

以下是摘要:

server {
    listen ...;
    server_name $DOMAINS;

    recursive_error_pages on;

    # First, try "Upstream A"
    location / {
        error_page 418 = @backend;
        return 418;
    }

    # Define "Upstream A"
    location @backend {
        proxy_pass http://$IP:81;
        proxy_set_header  X-Real-IP     $remote_addr;
        # Add your proxy_* options here
    }

    # On error, go to "Upstream B"
    error_page 502 @handle_502;

    # Fallback static error page, in case "Upstream B" fails
    root /home/nginx/www;
    location = /_static_error.html {
        internal;
    }

    # Define "Upstream B"
    location @handle_502 { # What to do when the backend server is not up
        proxy_pass ...;
        # Add your proxy_* options here
        proxy_intercept_errors on;          # Look at the error codes returned from "Upstream B"
        error_page 502 /_static_error.html; # Fallback to error page if "Upstream B" is down
        error_page 451 = @backend;          # Try "Upstream A" again
    }
}

原始答案/研究日志如下:


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

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为关闭,因此它不会在连续请求中按顺序尝试服务器。我尝试添加weight=1000000000 fail_timeout=1到第一个条目,但没有效果。到目前为止,我还没有找到任何不涉及客户端重定向的解决方案。


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


编辑:我让它工作了!除了error_page上述修复之外,只需启用recursive_error_pages

答案2

您可以尝试以下方法

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;
    }

}

相关内容