Python裝飾器實戰:實現優雅的重試機制

重試機制在編程中是比較常見的場景,主要被用於處理那些可能由於臨時性故障或網絡波動等原因而失敗的操作。

本文介紹如何通過Python裝飾器來實現重試機制
從而能夠在儘量少修改現有代碼的基礎上,給其中某些函數加上重試機制

1. 概要

關於Python的裝飾器,只是一個語法糖,原理也比較簡單,這裏不在贅述。

關於爲什麼要用重試機制
首先,它能顯著提高了系統的穩定性和可靠性。
因爲,在分佈式系統、網絡通信或任何涉及外部資源調用的場景中,失敗和異常是難以避免的。
通過引入重試機制,系統能夠在遇到這些臨時性故障時自動恢復,減少因單次失敗導致的整體服務中斷。

其次,重試機制有助於提升用戶體驗。
對於用戶來說,如果系統因爲一次網絡抖動或短暫的服務器不可用就拋出錯誤,那麼用戶可能會感到不滿。
通過重試機制,系統可以在用戶幾乎無感知的情況下恢復服務,從而提升用戶體驗。

此外,重試機制還可以幫助系統更好地應對突發的高負載或資源緊張的情況。
當系統面臨大量請求或資源爭用時,某些操作可能會因爲資源不足而失敗。
通過合理設置重試間隔和重試次數,系統可以平滑地處理這些突發情況,避免因爲短暫的資源不足而導致服務崩潰。

2. 實現重試機制

下面是我目前在用的一個重試裝飾器:

from functools import wraps
from time import sleep


def retry(retries: int = 3, delay: float = 1):
    """
    函數執行失敗時,重試

    :param retries: 最大重試的次數
    :param delay: 每次重試的間隔時間,單位 秒
    :return:
    """

    # 校驗重試的參數,參數值不正確時使用默認參數
    if retries < 1 or delay <= 0:
        retries = 3
        delay = 1

    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            # 第一次正常執行不算重試次數,所以retries+1
            for i in range(retries + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    # 檢查重試次數
                    if i == retries:
                        print(f"Error: {repr(e)}")
                        print(f'"{func.__name__}()" 執行失敗,已重試{retries}次')
                        break
                    else:
                        print(
                            f"Error: {repr(e)},{delay}秒後第[{i+1}/{retries}]次重試..."
                        )
                        sleep(delay)

        return wrapper

    return decorator

這個裝飾器有兩個參數,一個是重試次數(retries),一個是每次重試的間隔(delay)。
代碼比較簡單,通過捕獲函數func的異常來重試,重試次數達到最大重試次數後退出。

3. 使用重試機制示例

使用上面裝飾器的示例:

from decorators import retry
import time


@retry(retries=2, delay=2)
def pay():
    now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    print(f"[{now}]: 開始調用支付接口")
    raise Exception("調用支付接口超時...")


@retry(retries=5, delay=1)
def third():
    now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    print(f"[{now}]: 開始調用第三方接口")
    raise Exception("調用第三方接口超時...")


if __name__ == "__main__":
    pay()  # 重試2次,每次間隔2秒
    third()  # 重試5次,每次間隔1秒

模擬一個支付接口,一個調用第三方的接口,分別看看重試的效果。
image.png
簡簡單單給函數加一個@retry,就有了重試功能。

4. 總結

總之,在設計和開發系統時,合理地引入和應用重試機制是非常必要的,尤其是需要大量調用第三方服務的時候。
通過裝飾器的方式來實現重試機制,能夠儘量少的侵入代碼的業務邏輯,是一種優雅靈活的方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章