退避,指的是操作失敗,等一段時間再重試,而不是立即重試的行為。其意義在於期望被調用的資源能夠在這段時間裡自行恢復。
退避和重試的函數裝飾器
此模塊提供函數裝飾器,該函數裝飾器可用於包裝函數,以便函數重試直到滿足某些條件為止。當訪問可能出現間歇性故障的不可靠資源(如網絡資源和外部API)時,它應該有用。一般來說,它還可以用於外部生成內容的動態輪詢資源。
裝飾器既支持同步代碼的常規功能,也支持異步代碼的asyncio的協同程序。
示例
由於Kenneth Reitz的requests模塊已經成為Python中同步HTTP客戶端的行業標準,因此下面使用該模塊編寫了網絡示例,但是backoff模塊根本不需要它。
@backoff.on_exception
當指定異常被引發時,使用on_exception裝飾器重試。這裡有一個例子,當出現任何requests異常時,使用指數退避(即退避時間指數增長):
對於需要多個異常類型採取相同退避行為的情況,裝飾器還將接受異常元組:
放棄的條件
可選參數可以指定要放棄的條件。
關鍵字參數max_time指定在放棄之前的總時間的最大秒數。
關鍵字參數max_tries指定在放棄之前對目標函數進行調用的最大次數。
在某些情況下,可能需要檢查引發異常的實例本身,以確定它是否滿足可重試條件。giveup關鍵字參數可用於指定一個函數,該函數接受異常並在不應該重試異常時返回真值:
當放棄事件發生時,問題中的異常重新引發,因此調用on_exception-decorated函數的代碼可能仍然需要執行異常處理。
@backoff.on_predicate
當目標函數返回值符合某個特定條件時,on_predicate裝飾器會安排重試。當為外部生成內容輪詢資源時,這可能是有用的。
下面是一個例子,當目標函數的返回值為空列表時,使用斐波那契序列退避(退避時間滿足):
在初始化等待生成器時傳遞額外的關鍵字參數,因此在初始化fibo生成器時,上面的max_value參數作為關鍵字參數傳遞。
當未指定斷言函數時,默認做返回值的假值測試,因此上面可以寫的更簡潔些:
更簡單地說,一個繼續每秒鐘輪詢直到得到非假結果的函數可以像這樣定義:
Jitter
jitter算法可以向任何一個退避裝飾器提供jitter關鍵字參數。這個參數應該是一個函數,接受原始的退避值並返回它的jittered的對應項。
從1.2版開始,默認的jitter函數backoff.full_jitter實現了AWS架構博客的指數退避和Jitter帖子中定義的“Full Jitter”算法。注意,使用此算法,等待生成器所產生的時間實際上是等待的最大時間量。
以前版本的退避默認添加隨機數毫秒(高達1s)到原始睡眠值。如果需要,這種行為現在可以作為backoff.random_jitter。
使用多個裝飾器
還可以組合退避裝飾器以指定不同情況下的不同退避行為:
運行時配置
裝飾函數on_exception和on_predicate通常在導入時確定參數。當關鍵字參數作為常量值傳遞時,這很好,但是假設我們想要查閱帶有配置選項的字典,這些配置選項僅在運行時可用。相關值在導入時不可用。取而代之的是,裝飾函數可以通過在運行時執行函數來獲得參數值:
事件處理
兩個backoff裝飾器都可以選擇使用關鍵字參數on_success、on_backoff和on_giveup接受事件處理程序函數。這在報告統計或執行其他自定義日誌方面可能有用。
處理程序必須是一個接受一個字典參數的一元簽名的可調用文件。此字典包含調用的詳細信息。有效鍵包括:
target:引用調用的函數或方法
args:func的位置參數
kwargs : func的關鍵字參數
tries: 到目前為止的調用次數
elapsed: 到現在為止經過的時間
wait: 等待秒(僅on_backoff處理)
value: 觸發退避值(僅用on_predicate裝飾器)
打印回退事件的細節的處理程序可以實現如下:
每個事件類型的多個處理程序
在所有情況下,處理函數的迭代被接受,依次調用它們。例如,你可以提供一個簡單的處理函數列表,作為on_backoff關鍵字參數的值:
獲取異常信息
在on_exception裝飾器的情況下,所有on_backoff和on_giveup處理程序都從異常塊中調用,以處理異常。因此,異常信息可以通過python標準庫(特別是sys.exc_info()或traceback模塊)提供給處理函數。
異步代碼
要在基於asyncio的異步代碼中使用backoff,您只需要將backoff.on_exception或backoff.on_predicate應用於協同例程。您還可以使用接口是相同的on_success、on_backoff和on_giveup事件處理程序的協同程序。
下面的示例使用aiohttp 異步HTTP Client/Server庫。
採用Python 3.5及以上的async def和await語法:
如果使用Python 3.4,可以使用@asyncio.coroutine和yield from:
日誌配置
錯誤和回退和重試嘗試被記錄到“退避”記錄器。默認情況下,此記錄器配置為Handler,因此除非配置處理程序,否則將沒有輸出。從編程上講,這可以用簡單的方法來完成:
記錄默認級別是INFO,它對應於重試事件發生時的日誌記錄。如果您只希望在發生giveup事件時記錄,則將記錄器級別設置為ERROR。
英文原文:https://github.com/litl/backoff/
譯者:張新英
閱讀更多 Python部落 的文章