Nginx 失敗重試機制 詳細

2021-10-18 03:51:21 字數 4007 閱讀 4333

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_timeoutproxy_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...