Python退避及重試裝飾器:backoff

Python退避及重试装饰器:backoff

退避,指的是操作失敗,等一段時間再重試,而不是立即重試的行為。其意義在於期望被調用的資源能夠在這段時間裡自行恢復。

退避和重試的函數裝飾器

此模塊提供函數裝飾器,該函數裝飾器可用於包裝函數,以便函數重試直到滿足某些條件為止。當訪問可能出現間歇性故障的不可靠資源(如網絡資源和外部API)時,它應該有用。一般來說,它還可以用於外部生成內容的動態輪詢資源。

裝飾器既支持同步代碼的常規功能,也支持異步代碼的asyncio的協同程序。

示例

由於Kenneth Reitz的requests模塊已經成為Python中同步HTTP客戶端的行業標準,因此下面使用該模塊編寫了網絡示例,但是backoff模塊根本不需要它。

@backoff.on_exception

當指定異常被引發時,使用on_exception裝飾器重試。這裡有一個例子,當出現任何requests異常時,使用指數退避(即退避時間指數增長):

Python退避及重试装饰器:backoff

對於需要多個異常類型採取相同退避行為的情況,裝飾器還將接受異常元組:

Python退避及重试装饰器:backoff

放棄的條件

可選參數可以指定要放棄的條件。

關鍵字參數max_time指定在放棄之前的總時間的最大秒數。

Python退避及重试装饰器:backoff

關鍵字參數max_tries指定在放棄之前對目標函數進行調用的最大次數。

Python退避及重试装饰器:backoff

在某些情況下,可能需要檢查引發異常的實例本身,以確定它是否滿足可重試條件。giveup關鍵字參數可用於指定一個函數,該函數接受異常並在不應該重試異常時返回真值:

Python退避及重试装饰器:backoff

當放棄事件發生時,問題中的異常重新引發,因此調用on_exception-decorated函數的代碼可能仍然需要執行異常處理。

@backoff.on_predicate

當目標函數返回值符合某個特定條件時,on_predicate裝飾器會安排重試。當為外部生成內容輪詢資源時,這可能是有用的。

下面是一個例子,當目標函數的返回值為空列表時,使用斐波那契序列退避(退避時間滿足):

Python退避及重试装饰器:backoff

在初始化等待生成器時傳遞額外的關鍵字參數,因此在初始化fibo生成器時,上面的max_value參數作為關鍵字參數傳遞。

當未指定斷言函數時,默認做返回值的假值測試,因此上面可以寫的更簡潔些:

Python退避及重试装饰器:backoff

更簡單地說,一個繼續每秒鐘輪詢直到得到非假結果的函數可以像這樣定義:

Python退避及重试装饰器:backoff

Jitter

jitter算法可以向任何一個退避裝飾器提供jitter關鍵字參數。這個參數應該是一個函數,接受原始的退避值並返回它的jittered的對應項。

從1.2版開始,默認的jitter函數backoff.full_jitter實現了AWS架構博客的指數退避和Jitter帖子中定義的“Full Jitter”算法。注意,使用此算法,等待生成器所產生的時間實際上是等待的最大時間量。

以前版本的退避默認添加隨機數毫秒(高達1s)到原始睡眠值。如果需要,這種行為現在可以作為backoff.random_jitter。

使用多個裝飾器

還可以組合退避裝飾器以指定不同情況下的不同退避行為:

Python退避及重试装饰器:backoff

運行時配置

裝飾函數on_exception和on_predicate通常在導入時確定參數。當關鍵字參數作為常量值傳遞時,這很好,但是假設我們想要查閱帶有配置選項的字典,這些配置選項僅在運行時可用。相關值在導入時不可用。取而代之的是,裝飾函數可以通過在運行時執行函數來獲得參數值:

Python退避及重试装饰器:backoff

事件處理

兩個backoff裝飾器都可以選擇使用關鍵字參數on_success、on_backoff和on_giveup接受事件處理程序函數。這在報告統計或執行其他自定義日誌方面可能有用。

處理程序必須是一個接受一個字典參數的一元簽名的可調用文件。此字典包含調用的詳細信息。有效鍵包括:

  • target:引用調用的函數或方法

  • args:func的位置參數

  • kwargs : func的關鍵字參數

  • tries: 到目前為止的調用次數

  • elapsed: 到現在為止經過的時間

  • wait: 等待秒(僅on_backoff處理)

  • value: 觸發退避值(僅用on_predicate裝飾器)

打印回退事件的細節的處理程序可以實現如下:

Python退避及重试装饰器:backoff

每個事件類型的多個處理程序

在所有情況下,處理函數的迭代被接受,依次調用它們。例如,你可以提供一個簡單的處理函數列表,作為on_backoff關鍵字參數的值:

Python退避及重试装饰器: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退避及重试装饰器:backoff

如果使用Python 3.4,可以使用@asyncio.coroutine和yield from:

Python退避及重试装饰器:backoff

日誌配置

錯誤和回退和重試嘗試被記錄到“退避”記錄器。默認情況下,此記錄器配置為Handler,因此除非配置處理程序,否則將沒有輸出。從編程上講,這可以用簡單的方法來完成:

Python退避及重试装饰器:backoff

記錄默認級別是INFO,它對應於重試事件發生時的日誌記錄。如果您只希望在發生giveup事件時記錄,則將記錄器級別設置為ERROR。

Python退避及重试装饰器:backoff
英文原文:https://github.com/litl/backoff/
譯者:張新英


分享到:


相關文章: