nginx 的失敗重試,就是為了實現對客戶端透明的伺服器高可用。然而這部分失敗重試機制比較複雜且官方文件沒詳細介紹,本文將對其解析,並配合實際場景例子使之更容易被理解。
這部分介紹最常見、最基礎的失敗重試場景。為了方便理解,使用了以下配置進行分析(
proxy_next_upstream
沒有特殊配置):
upstream test
模擬後端異常的方式是直接將對應服務關閉,造成 connect refused 的情況,對應error
錯誤。
在最初始階段,所有伺服器都正常,請求會按照輪詢方式依次**給 ab 兩個 server 處理。假設這時 a 節點服務崩潰,埠不通,則會出現這種情況:
請求 1 轉到 a 異常,再重試到 b 正常處理,a fails +1
請求 2 轉到 b 正常處理
請求 3 轉到 a 異常,再重試到 b 正常處理,a fails +1 達到 max_fails 將被遮蔽 60s
遮蔽 a 的期間請求都只轉給 b 處理,直到遮蔽到期後將 a 恢復重新加入存活列表,再按照這個邏輯執行
如果在 a 的遮蔽期還沒結束時,b 節點的服務也崩潰,埠不通,則會出現:
請求 1 轉到 b 異常,此時所有線上節點異常,會出現:
預設配置是沒有做重試機制進行限制的,也就是會盡可能去重試直至失敗。為了方便理解,使用以下配置進行說明(只列出關鍵配置):
proxy_connect_timeout 3s;
proxy_next_upstream_timeout 6s;
proxy_next_upstream_tries 3;
upstream test
第 2~3 行表示在 6 秒內允許重試 3 次,只要超過其中任意乙個設定,nginx 會結束重試並返回客戶端響應(可能是錯誤碼)。我們通過 iptables drop 掉對 8001、8002 埠的請求來模擬 connect timeout 的情況:
iptables -i input -p tcp -m tcp --dport 8001 -j drop
iptables -i input -p tcp -m tcp --dport 8002 -j drop
則具體的請求處理情況如下:
請求 1 到達 nginx,按照以下邏輯處理
從上面的例子,可以看出proxy_next_upstream_timeout
配置項對重試機制的限制,重試次數的情況也是類似,這裡就不展開細講了。
nginx 支援設定備用節點,當所有線上節點都異常時啟用備用節點,同時備用節點也會影響到失敗重試的邏輯,因此單獨列出來介紹。upstream 的配置中,可以通過
backup
指令來定義備用伺服器,其含義如下:
正常情況下,請求不會轉到到 backup 伺服器,包括失敗重試的場景
當所有正常節點全部不可用時,backup 伺服器生效,開始處理請求
一旦有正常節點恢復,就使用已經恢復的正常節點
backup 伺服器生效期間,不會存在所有正常節點一次性恢復的邏輯如果全部 backup 伺服器也異常,則會將所有節點一次性恢復,加入存活列表
如果全部節點(包括 backup)都異常了,則 nginx 返回 502 錯誤
為了方便理解,使用了以下配置進行說明:
upstream test
在最初始階段,所有伺服器都正常,請求會按照輪詢方式依次**給 ab 兩個節點處理。當只有 a 異常的情況下,與上文沒有 backup 伺服器場景處理方式一致,這裡就不重複介紹了。
假設在 a 的遮蔽期還沒結束時,b 節點的服務也崩潰,埠不通,則會出現:
請求 1 轉到 b 處理,異常,此時所有線上節點異常,會出現:
假設 ab 的遮蔽期都還沒結束時,c 節點的服務也崩潰,埠不通,則會出現
請求 1 轉到 c 異常,此時所有節點(包括 backup)都異常,會出現:
如果不熟悉 http 協議,以及 nginx 的重試機制,很可能在使用過程中踩了各種各樣的坑:
以下整理了一些常見的坑,以及應對策略。
介面的 post 請求允許重試,但實際使用中卻沒有出現重試,直接報錯。從 1.9.13 版本,nginx 不再會對乙個非冪等的請求進行重試。如有需要,必須在
proxy_next_upstream
配置項中顯式指定non_idempotent
配置。參考 rfc-2616 的定義:
proxy_next_upstream error timeout non_idemponent;
該配置需要注意的點:
新增非冪等請求重試是追加引數值,不要把原來預設的 error/timeout 引數值去掉
必須明確自己的業務允許非冪等請求重試以避免業務異常
一些場景不希望請求在多個上游進行重試,即使上游伺服器完全掛掉。正常情況下,nginx 會對 error、timeout 的失敗進行重試,對應預設配置如下:
proxy_next_upstream error timeout;
如希望完全禁止重試,需要顯式指定配置來關閉重試機制,配置如下:
proxy_next_upstream off;
錯誤配置了重試引數導致 nginx **效能出現異常預設的 error/timeout 是不會出現這種問題的。在定義重試場景時,需要結合業務情況來確定是否啟用自定義錯誤重試,而不是單純去複製其他服務的配置。比如對於某個業務,沒有明確自己業務情況,去網上覆制了 nginx 配置,其中包括了:
proxy_next_upstream error timeout invalid_header http_500 http_503 http_404;
那麼只需要隨便定義乙個不存在的 uri 去訪問該服務頻繁去請求該服務,就可以重複觸發 nginxno live upstreams
報錯,這在業務高峰情況下,效能將受到極大影響。同樣,如果使用的**框架存在不標準 http 處理響應情況,惡意構造的請求同樣也會造成類似效果。
因此在配置重試機制時,必須先對業務的實際情況進行分析,嚴謹選擇重試場景。
某冪等介面處理請求耗時較長,出現非預期的重試導致乙個請求被多次響應處理。假設該介面處理請求平均需要 30s,而對應的**超時為:
proxy_read_timeout 30s;
預設的重試包含了 timeout 場景,在這個場景下,可能會有不到一半的請求出現超時情況,同時又因為是冪等請求,所有會進行重試,最終導致乙個的超時請求會被發到所有節點處理的請求放大情況。
因此在進行超時設定時,也必須要跟進業務實際情況來調整。可以適當調大超時設定,並收集請求相關耗時情況進行統計分析來確定合理的超時時間。
因上游伺服器異常導致連線問題,客戶端無超時機制,導致請求耗時非常久之後才失敗。已知所有上游伺服器異常,無法連線或需要非常久(超過 10s)才能連線上,假設配置了連線超時為:
proxy_connect_timeout 10;
在這種情況下,因客戶端無超時設定,冪等請求將卡住 10*n 秒後超時(n 為上游伺服器數量)。因此建議:
客戶端設定請求超時時間
配置合理的proxy_connect_timeout
配合proxy_next_upstream_timeout
、proxy_next_upstream_tries
來避免重試導致更長超時
Python失敗重試機制
import unittest from time import sleep from retrying import retry from selenium import webdriver class testretry unittest.testcase def setup self none...
nginx的重試機制
現在對外服務的 很少只使用乙個服務節點,而是部署多台伺服器,上層通過一定機制保證容錯和負載均衡。nginx就是常用的一種http和反向 伺服器,支援容錯和負載均衡。nginx的重試機制就是容錯的一種。在nginx的配置檔案中,proxy next upstream項定義了什麼情況下進行重試,官網文件...
中斷重試機制
原文 中斷重試 中斷重試機制 public abstract class retrytemplate public retrytemplate setsleeptime int sleeptime this sleeptime sleeptime return this public intgetr...