如何讓你的 Python 代碼更加的健壯
在編程的時(shí)候,我們難免會(huì)遇到一些不可靠的情況,比如網(wǎng)絡(luò)請(qǐng)求失敗,數(shù)據(jù)庫(kù)連接超時(shí)等等。這些不確定性會(huì)讓我們的程序容易出現(xiàn)各種錯(cuò)誤和異常。那么如何來(lái)增加程序的容錯(cuò)性和健壯性呢?
可能大多數(shù)人會(huì)想到使用try except來(lái)進(jìn)行異常捕捉進(jìn)行失敗重試(Retry)。雖然try-escept一個(gè)非常常見(jiàn)和有效的方式來(lái)增強(qiáng)程序穩(wěn)定性,但是可能一不小心就會(huì)造成棧溢出。
所以接下來(lái)我就來(lái)介紹一個(gè)另外的一個(gè)專門用于失敗重試的庫(kù):retrying。
一、定義
在Python生態(tài)中,retrying庫(kù)提供了非常便捷的裝飾器和函數(shù)來(lái)幫助我們輕松添加失敗重試機(jī)制。它可以自定義重試策略、停止條件、等待間隔等,對(duì)各種異常進(jìn)行捕捉處理。使用retrying可以大大減少我們重復(fù)編寫失敗重試輪詢的代碼量。
1. 下載retrying
pip install retrying
2. 無(wú)參數(shù)重試
我們可以直接在函數(shù)上使用裝飾器@retry來(lái)進(jìn)行失敗重試
import retrying
@retry
def func():
for item in range(0,100):
result=item / 0
print(result)
return result
func()
但是這種方式并不建議使用,就像上面的代碼,我們都知道0作為除數(shù)就會(huì)報(bào)錯(cuò),在上面的func函數(shù)中,因?yàn)榧恿薂retry裝飾器進(jìn)行失敗重試,這樣就就會(huì)進(jìn)入一個(gè)死循環(huán)一直失敗一直重試。
所以我們?cè)谶M(jìn)行失敗重試的時(shí)候最好是需要加上一些參數(shù)來(lái)限制失敗重試。
3. 有參數(shù)重試
(1) stop_max_attempt_number
在retry中傳入stop_max_attempt_number參數(shù)后可以指定失敗重試的次數(shù)
@retry(stop_max_attempt_number=2)
def func():
print(f"記錄失敗重試")
for item in range(0,100):
result=item / 0
print(result)
return result
func()
因?yàn)檫@里我們指定了失敗后進(jìn)行兩次重試,如果重試執(zhí)行兩次后還是報(bào)錯(cuò)則結(jié)束重試,將錯(cuò)誤信息拋出來(lái)。
(2) wait_fixed傳入wati_fixed后,可以指定重試的時(shí)間
from retrying import retry
import time
# 設(shè)置三秒重試一次
@retry(wait_fixed=3000)
def func():
print(f"記錄失敗重試:",time.strftime("%Y-%m-%d %H:%M:%S"))
result=1 / 0
print(result)
return result
func()
配置重試間隔時(shí)間后,成語(yǔ)遇到執(zhí)行失敗或者報(bào)錯(cuò)后,就會(huì)根據(jù)設(shè)置的重試時(shí)間去進(jìn)行重試執(zhí)行
(3) wait_random_min和wait_random_max
通常wait_random_min和wait_random_max是一起搭配使用的,可以設(shè)置一個(gè)重試等待的時(shí)間,然后會(huì)在設(shè)置的時(shí)間區(qū)間內(nèi)隨機(jī)取一個(gè)等待時(shí)間進(jìn)行重試
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_multiplier和wait_exponential_max
官方解釋為:以指數(shù)的形式產(chǎn)生兩次retrying之間的停留時(shí)間, 產(chǎn)生的值為2^previous_attempt_number * wait_exponential_multiplier, previous_attempt_number是前面已經(jīng)retry的次數(shù), 如果產(chǎn)生的這個(gè)值超過(guò)了wait_exponential_max的大小, 那么之后兩個(gè)retrying之間的停留值都為wait_exponential_max.
通俗來(lái)點(diǎn)講就是每次重試的時(shí)間以wait_exponential_multiplier設(shè)置的值2,如果重試后還是失敗則繼續(xù)2,直到最后的值等于或則超過(guò)wait_exponential_max設(shè)置的值后,后面的每一次重試等待時(shí)間都是wait_exponential_max設(shè)置的值.
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
在前面介紹的參數(shù)都是如何配置失敗沖重試的等待時(shí)間或者重試次數(shù)之類的,但是我們不能時(shí)時(shí)刻刻盯著程序,在程序代碼發(fā)生錯(cuò)誤時(shí)我們應(yīng)該要進(jìn)行發(fā)送短信或者郵件之類的提醒才行.
在這里就可以使用到wait_func參數(shù),它接收一個(gè)可執(zhí)行函數(shù),返回一個(gè)具體的間隔時(shí)間數(shù)值,單位ms。接收的函數(shù)須接收兩個(gè)參數(shù):attempt_number當(dāng)前運(yùn)行次數(shù),delay_since_first_attempt_ms當(dāng)前重試機(jī)制運(yùn)行時(shí)間(單位ms).
from retrying import retry
import time
def func_demo(attempt_number,delay_since_first_attempt_ms):
print("函數(shù)運(yùn)行失敗后運(yùn)行該函數(shù)")
if attempt_number == 5:
print("已經(jīng)重試失敗五次了,開(kāi)始準(zhǔn)備發(fā)送提醒")
if attempt_number == 10:
print("已經(jīng)重試失敗超10次了,發(fā)送郵件給相關(guān)人員緊急處理")
if attempt_number >10:
print("重試時(shí)間過(guò)長(zhǎng),做一些其他臨時(shí)方案進(jìn)行補(bǔ)救")
# return一個(gè)重試的時(shí)間
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通過(guò)調(diào)用其他可執(zhí)行的函數(shù),我們可以借助它來(lái)做一些臨時(shí)的補(bǔ)救措施,避免程序一直無(wú)法運(yùn)行而產(chǎn)生的影響。
(6) 其他參數(shù)
在retry中還存在有很多參數(shù),有興趣的小伙伴可以去詳細(xì)了解下:
- stop_max_attempt_number:在停止之前嘗試的最大次數(shù),最后一次如果還是有異常則會(huì)拋出異常,停止運(yùn)行,默認(rèn)為5次
- stop_max_delay:最大延遲時(shí)間,大概意思就是:如果調(diào)用的函數(shù)出現(xiàn)異常,那么就會(huì)重復(fù)調(diào)用這個(gè)函數(shù),最大調(diào)用時(shí)間,默認(rèn)為100毫秒
- wait_fixed:兩次調(diào)用方法期間停留時(shí)長(zhǎng), 如果出現(xiàn)異常則會(huì)一直重復(fù)調(diào)用,默認(rèn) 1000毫秒
- wait_random_min:在兩次調(diào)用方法停留時(shí)長(zhǎng),停留最短時(shí)間,默認(rèn)為0
- wait_random_max:在兩次調(diào)用方法停留時(shí)長(zhǎng),停留最長(zhǎng)時(shí)間,默認(rèn)為1000毫秒
- wait_incrementing_increment:每調(diào)用一次則會(huì)增加的時(shí)長(zhǎng),默認(rèn) 100毫秒
- wait_exponential_multiplier和wait_exponential_max:以指數(shù)的形式產(chǎn)生兩次「retrying」之間的停留時(shí)間,產(chǎn)生的值為2^previous_attempt_number * wait_exponential_multiplier,previous_attempt_number是前面已經(jīng)「retry」的次數(shù),如果產(chǎn)生的這個(gè)值超過(guò)了wait_exponential_max的大小,那么之后兩個(gè)「retrying」之間的停留值都為wait_exponential_max
- retry_on_exception: 指定一個(gè)函數(shù),如果此函數(shù)返回指定異常,則會(huì)重試,如果不是指定的異常則會(huì)退出
- retry_on_result:指定一個(gè)函數(shù),如果指定的函數(shù)返回True,則重試,否則拋出異常退出
- wrap_exception:參數(shù)設(shè)置為True/False,如果指定的異常類型,包裹在RetryError中,會(huì)看到RetryError和程序拋的Exception error
- stop_func: 每次拋出異常時(shí)都會(huì)執(zhí)行的函數(shù),如果和stop_max_delay、stop_max_attempt_number配合使用,則后兩者會(huì)失效 (指定的stop_func會(huì)有兩個(gè)參數(shù):attempts, delay)
- wait_func:和stop_func用法差不多。