Python 系列:如何提高 python 程式程式碼的健壯性

大话性能發表於2024-05-21

前言

在程式設計的時候,我們難免會遇到一些不可靠的情況,比如網路請求失敗,資料庫連線超時等等。這些不確定性會讓我們的程式容易出現各種錯誤和異常。那麼如何來增加程式的容錯性和健壯性呢?

可能大多數人會想到使用 try except 來進行異常捕捉進行失敗重試 (Retry)。雖然 try-escept 一個非常常見和有效的方式來增強程式穩定性,但是可能一不小心就會造成棧溢位。

所以接下來我就來介紹一個另外的一個專門用於失敗重試的庫:retrying

定義

在 Python 生態中,retrying 庫提供了非常便捷的裝飾器和函式來幫助我們輕鬆新增失敗重試機制。它可以自定義重試策略、停止條件、等待間隔等,對各種異常進行捕捉處理。使用 retrying 可以大大減少我們重複編寫失敗重試輪詢的程式碼量。

1.下載 retrying

pip install retrying

2.無引數重試

我們可以直接在函式上使用裝飾器@retry來進行失敗重試

import retrying
@retry
def func():
    for item in range(0,100):
        result=item / 0
        print(result)
        return result

func()

但是這種方式並不建議使用,就像上面的程式碼,我們都知道 0 作為除數就會報錯,在上面的 func 函式中,因為加了@retry裝飾器進行失敗重試,這樣就就會進入一個死迴圈一直失敗一直重試。

所以我們在進行失敗重試的時候最好是需要加上一些引數來限制失敗重試。

3.有引數重試

(1)stop_max_attempt_number

在 retry 中傳入 stop_max_attempt_number 引數後可以指定失敗重試的次數

@retry(stop_max_attempt_number=2)
def func():
    print(f"記錄失敗重試")
    for item in range(0,100):
        result=item / 0
        print(result)
        return result

func()

因為這裡我們指定了失敗後進行兩次重試,如果重試執行兩次後還是報錯則結束重試,將錯誤資訊丟擲來。

(2)wait_fixed傳入 wati_fixed 後,可以指定重試的時間

from retrying import retry
import time

# 設定三秒重試一次
@retry(wait_fixed=3000)  
def func():
    print(f"記錄失敗重試:",time.strftime("%Y-%m-%d %H:%M:%S"))

    result=1 / 0
    print(result)
    return result

func()

配置重試間隔時間後,成語遇到執行失敗或者報錯後,就會根據設定的重試時間去進行重試執行

(3)wait_random_minwait_random_max

通常 wait_random_min 和 wait_random_max 是一起搭配使用的,可以設定一個重試等待的時間,然後會在設定的時間區間內隨機取一個等待時間進行重試

from retrying import retry
import time


@retry(wait_random_min=1000,wait_random_max=9000)
def func():
    print(f"記錄失敗重試:",time.strftime("%Y-%m-%d %H:%M:%S"))

    result=1 / 0
    print(result)
    return result

func()

(4)wait_exponential_multiplierwait_exponential_max

官方解釋為:以指數的形式產生兩次 retrying 之間的停留時間, 產生的值為 2previous_attempt_number * wait_exponential_multiplier, previous_attempt_number 是前面已經 retry 的次數, 如果產生的這個值超過了 wait_exponential_max 的大小, 那麼之後兩個 retrying 之間的停留值都為 wait_exponential_max

通俗來點講就是每次重試的時間以 wait_exponential_multiplier 設定的值 *2,如果重試後還是失敗則繼續 *2,直到最後的值等於或則超過 wait_exponential_max 設定的值後,後面的每一次重試等待時間都是 wait_exponential_max 設定的值

from retrying import retry
import time

@retry(wait_exponential_multiplier=1000,wait_exponential_max=10000)
def func():
    print(f"記錄失敗重試:",time.strftime("%Y-%m-%d %H:%M:%S"))

    result=1 / 0
    print(result)
    return result

func()

(5) wait_func

在前面介紹的引數都是如何配置失敗衝重試的等待時間或者重試次數之類的,但是我們不能時時刻刻盯著程式,在程式程式碼發生錯誤時我們應該要進行傳送簡訊或者郵件之類的提醒才行

在這裡就可以使用到 wait_func 引數,它接收一個可執行函式,返回一個具體的間隔時間數值,單位 ms。接收的函式須接收兩個引數:attempt_number 當前執行次數,delay_since_first_attempt_ms 當前重試機制執行時間 (單位 ms)

from retrying import retry
import time


def func_demo(attempt_number,delay_since_first_attempt_ms):
    print("函式執行失敗後執行該函式")

    if attempt_number == 5:
        print("已經重試失敗五次了,開始準備傳送提醒")

    if attempt_number == 10:
        print("已經重試失敗超10次了,傳送郵件給相關人員緊急處理")

    if attempt_number >10:
        print("重試時間過長,做一些其他臨時方案進行補救")

    # return一個重試的時間
    return 2000


@retry(wait_func=func_demo)
def func():
    print(f"記錄失敗重試:",time.strftime("%Y-%m-%d %H:%M:%S"))

    result=1 / 0

    return result

func()

使用 wait_func 透過呼叫其他可執行的函式,我們可以藉助它來做一些臨時的補救措施,避免程式一直無法執行而產生的影響。

(6)其他引數

在 retry 中還存在有很多引數,有興趣的小夥伴可以去詳細瞭解下

  • stop_max_attempt_number:在停止之前嘗試的最大次數,最後一次如果還是有異常則會丟擲異常,停止執行,預設為 5 次
  • stop_max_delay:最大延遲時間,大概意思就是:如果呼叫的函式出現異常,那麼就會重複呼叫這個函式,最大呼叫時間,預設為 100 毫秒
  • wait_fixed:兩次呼叫方法期間停留時長, 如果出現異常則會一直重複呼叫,預設 1000 毫秒
  • wait_random_min:在兩次呼叫方法停留時長,停留最短時間,預設為 0
  • wait_random_max:在兩次呼叫方法停留時長,停留最長時間,預設為 1000 毫秒
  • wait_incrementing_increment:每呼叫一次則會增加的時長,預設 100 毫秒
  • wait_exponential_multiplierwait_exponential_max:以指數的形式產生兩次「retrying」之間的停留時間,產生的值為 2previous_attempt_number * wait_exponential_multiplier,previous_attempt_number 是前面已經「retry」的次數,如果產生的這個值超過了 wait_exponential_max 的大小,那麼之後兩個「retrying」之間的停留值都為 wait_exponential_max
  • retry_on_exception: 指定一個函式,如果此函式返回指定異常,則會重試,如果不是指定的異常則會退出
  • retry_on_result:指定一個函式,如果指定的函式返回 True,則重試,否則丟擲異常退出
  • wrap_exception:引數設定為 True/False,如果指定的異常型別,包裹在 RetryError 中,會看到 RetryError 和程式拋的 Exception error
  • stop_func: 每次丟擲異常時都會執行的函式,如果和 stop_max_delay、stop_max_attempt_number 配合使用,則後兩者會失效 (指定的 stop_func 會有兩個引數:attempts, delay)
  • wait_func:和 stop_func 用法差不多。

更多內容可以學習《測試工程師 Python 工具開發實戰》書籍《大話效能測試 JMeter 實戰》書籍

相關文章