【Python 軍火庫🧨 - tenacity】升級裝備囉! 讓你寫出來的程式更加的強健

2023/09/05閱讀時間約 8 分鐘
圖片來源

圖片來源

我們開發程式的過程中難免會依賴DB或其他服務, 但複雜的網路環境下我們並沒有辦法確保我們發送的請求是否正確的送達, 因此我們可以在程式中加入Retry機制, 提升我們軟體的強健性。

然而在Python的程式語言裡正好有一個 tenacity 套件非常適合讓我們的Retry作業更加順利, 它是一個兼具彈性及可靠度的Python套件, 除了重試也支援超時機制, 讓我們不在苦於自行實作這些狀況。

這次我們會用個人挑戰為案例來進行生動的演示,讓你不再厭倦學習程式。

給自己一個挑戰

圖片來源

圖片來源

def do_everything():
# 我可以挑戰破關
# 我可以挑戰百岳
# 我還可以...
pass

do_everything()

過程總會發生一些狀況

圖片來源

圖片來源

我們的體力總是有限的, 如果沒有練習的狀況之下去挑戰高強度的百岳很容易體力不夠而無法登頂。

def do_everything():
# 爬山
# 小百岳
# 百岳
# 啊啊啊 遇到瓶頸了
raise Exception('體力有限...爬不動了...')

難道因為一次的體力不夠就此中斷嗎? 那前面的過程會非常可惜, 為了避免這種狀況我們可以…

我們是不是可以來點更可靠的輔助工具

其實爬山這項運動就是不斷的練習、挑戰、檢驗成果, 因此最適合「重試」這個過程, 但我們的重試也應該隨著每次的強度增加休息的時間, 而不是傻傻的爬完立刻重試, 那會沒有什麼效率, 因此我們嘗試導入「tenacity」來解決這個重試的難題, 就像是爬山的過程中增加一些裝備來輔助我們堅持下去一樣。

非常重要的關鍵因子

很重要所以要說三次, @retry、@retry、@retry,這是我們今天的重要主角,它會在我們每一個函式進行補強,透過靈活彈性的裝飾器崁入到我們每一個重要函式,為我們的產品加入一個重要的強鍵性補強。

from tenacity import retry

@retry(...)
def func(...):
...

停損要做好

❌ 基礎: 永遠不要放棄的重試

from tenacity import retry
@retry
def never_gonna_give_you_up():
print("很傻很天真, 重試到頭破血流、天荒地老")
raise Exception

✔️ 適時的停損策略1: 最多N次

from tenacity import retry, stop_after_attempt

@retry(stop=stop_after_attempt(5))
def stop_after_5_attempts():
print("超過5次就掰掰")
raise Exception

✔️ 適時的停損策略2: 不要把時間浪費在沒有結果的努力上

from tenacity import retry, stop_after_delay

@retry(stop=stop_after_delay(30))
def stop_after_30_s():
print("最多給你30秒")
raise Exception

重試之前觀望一下

等個3秒再出發

from tenacity import retry, wait_fixed

@retry(wait=wait_fixed(3))
def wait_3_s():
print("Wait 3 second between retries")
raise Exception

隨機的等待一下

from tenacity import retry, wait_random

@retry(wait=wait_random(min=2, max=4))
def wait_random_2_to_4_s():
print("隨機等個2~4秒再出發")
raise Exception

分佈式服務需要來點…

from tenacity import retry, wait_exponential

@retry(wait=wait_exponential(multiplier=2, min=1, max=10))
def wait_exponential_1():
print("2秒(1*2)、4秒(2*2)、8秒(4*2)...最多不超過10秒")
raise Exception

有條件的進行重試

遇到「什麼條件」的狀況才重試…

from tenacity import retry, retry_if_exception_type, retry_if_not_exception_type

class ClientError(Exception):
"""自訂某些錯誤型態"""

@retry(retry=retry_if_exception_type(IOError))
def io_error():
print("如果是IO類型的錯誤再重試")
raise Exception

@retry(retry=retry_if_not_exception_type(ClientError))
def client_error():
print("如果🚫 不是自訂類型的錯誤再重試")
raise Exception

到達極限了怎麼辦

雖然說努力重試是很棒, 但我們總會遇到極限, 前面我們也提到了即時止損的功能, 但除了即時止損之外, 我們還能做些什麼改變嗎?

from tenacity import retry, stop_after_attempt, retry_error_callback, retry_if_result

def return_last_value(retry_state):
"""return the result of the last call attempt"""
return retry_state.outcome.result()

def is_false(value):
"""Return True if value is False"""
return value is False

# will return False after trying 3 times to get a different result
@retry(stop=stop_after_attempt(3),
retry_error_callback=return_last_value,
retry=retry_if_result(is_false))
def eventually_return_false():
return False

結語

一個具有高品質的產品最重要的就是可靠性,尤其身處數位時代的我們,人人都勢必接觸到我們所開發的軟體,一旦可靠性不佳,輕則影響使用者體驗,重則導致損失,因此身兼軟體開發大任的我們務必要非常的小心,避免造成使用者的損失進而影響到商譽與信用,造成不可挽回的連鎖反應,最後最後,雲產品架構之下最不可控的就是網路(Network)的部分了,為了避免因為網路問題導致系統壞掉,可靠性就顯得格外重要,正好今天這個篇章能夠派上用場,請好好的收藏起來,如果有任何疑問也歡迎留言互相討論,一起加油進步。


您是否苦於網路資訊爆炸嗎? 教學何其多,但卻無法好好選擇的困境呢? 歡迎加入「🔒 阿Han的軟體心法實戰營」, 這裡不給您冗餘的雜訊, 單刀直入直接送您重點, 避開選擇障礙的困境, 讓您獲得業界標準的開發起手式, 成為Top 1的頂尖人才。

91會員
260內容數
哈囉,我是阿Han,是一位 👩‍💻 軟體研發工程師,喜歡閱讀、學習、撰寫文章及教學,擅長以圖代文,化繁為簡,除了幫助自己釐清思路之外,也希望藉由圖解的方式幫助大家共同學習,甚至手把手帶您設計出高品質的軟體產品。
留言0
查看全部
發表第一個留言支持創作者!