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 重定向發送到同一位置(以便客戶端將重試相同的請求,但現在後端伺服器已啟動)。然而,現在 nginx 愚蠢地用它從後端請求嘗試 (502) 獲得的狀態程式碼覆蓋狀態程式碼,儘管控制伺服器正在發送“位置”標頭。我終於通過將 error_page 行更改為“工作”了
error_page 502 =307 @handle_502;
,從而強制所有控制伺服器回复都以 307 程式碼發送回客戶端。這是非常hacky和不可取的,因為1)根據控制伺服器的響應,無法控制nginx下一步應該做什麼(理想情況下,我們只想在控制伺服器報告成功時重試後端),以及2)不是所有的HTTP客戶端支持 HTTP 重定向(例如 curl 使用者和使用 libcurl 的應用程序需要顯式啟用以下重定向)。讓 nginx 嘗試代理到上游伺服器 A,然後是 B,然後是 A 的正確方法是什麼(理想情況下,只有當 B 返回特定的狀態程式碼時)?
關鍵點:
- 不要
upstream
為故障轉移而煩惱,如果 ping 一個伺服器會啟動另一個伺服器 - 沒有辦法告訴 nginx(至少,不是 FOSS 版本)第一台伺服器再次啟動。nginx 將在第一個請求時按順序嘗試伺服器,但不會嘗試後續請求,儘管有任何backup
或weight
設置fail_timeout
。- 在使用命名位置實施故障轉移時,您必須啟用。
recursive_error_pages``error_page
- 啟用
proxy_intercept_errors
以處理從上游伺服器發送的錯誤程式碼。=
語法(例如error_page 502 = @handle_502;
)是正確處理指定位置中的錯誤程式碼所必需的。如果=
未使用,nginx 將使用上一個塊中的錯誤程式碼。這是一個摘要:
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
!