當咱們的代碼是有訪問網絡相關的操做時,好比http請求或者訪問遠程數據庫,常常可能會發生一些錯誤,有些錯誤可能從新去發送請求就會成功,本文分析常見可能須要重試的場景,並最後給出python代碼實現。html
常見異常分紅兩種,一種是請求傳輸過程出錯,另外一種是服務端負載太高致使錯誤。
對於第一種錯誤,可能請求還未到服務端處理程序就已經返回。
HTTP請求錯誤:python
訪問數據庫錯誤:mysql
1 Class 08 — Connection Exception 2 08000 connection_exception 3 08003 connection_does_not_exist 4 08006 connection_failure 5 08001 sqlclient_unable_to_establish_sqlconnection 6 08004 sqlserver_rejected_establishment_of_sqlconnection
對於第二類錯誤,服務器負載太高致使。對於HTTP請求,可根據狀態碼識別:git
對於數據庫訪問:github
constants for MySQL errors
the mapping between exception types in PyMYSQL and error codes.sql
本文以網絡IO爲例,利用python裝飾器實現重試機制。用fetch函數去發送http請求下載網頁
數據庫
# Example is taken from http://aiohttp.readthedocs.io/en/stable/#getting-started import aiohttp import asyncio async def fetch(session, url): async with session.get(url) as response: return await response.text() # Client code, provided for reference async def main(): async with aiohttp.ClientSession() as session: html = await fetch(session, 'http://python.org') print(html) loop = asyncio.get_event_loop() loop.run_until_complete(main())
fetch函數並非可靠的服務,可能存在失敗的狀況,這時候根據上文所列的狀況實現重試機制,代碼以下:
服務器
import aiohttp @retry(aiohttp.DisconnectedError, aiohttp.ClientError, aiohttp.HttpProcessingError) async def fetch(session, url): async with session.get(url) as response: return await response.text()
retry實現以下,利用裝飾器模式
網絡
import logging from functools import wraps log = logging.getLogger(__name__) def retry(*exceptions, retries=3, cooldown=1, verbose=True): """Decorate an async function to execute it a few times before giving up. Hopes that problem is resolved by another side shortly. Args: exceptions (Tuple[Exception]) : The exceptions expected during function execution retries (int): Number of retries of function execution. cooldown (int): Seconds to wait before retry. verbose (bool): Specifies if we should log about not successful attempts. """ def wrap(func): @wraps(func) async def inner(*args, **kwargs): retries_count = 0 while True: try: result = await func(*args, **kwargs) except exceptions as err: retries_count += 1 message = "Exception during {} execution. " \ "{} of {} retries attempted". format(func, retries_count, retries) if retries_count > retries: verbose and log.exception(message) raise RetryExhaustedError( func.__qualname__, args, kwargs) from err else: verbose and log.warning(message) if cooldown: await asyncio.sleep(cooldown) else: return result return inner return wrap
基本思想是在達到重試次數限制以前捕獲預期的異常。在每次執行之間,等待固定時間。此外,若是咱們想要詳細,會寫每一個失敗嘗試的日誌。固然,本例子只提供了幾個重試選項,一個完備的重試庫應該提供更多重試配置,好比指數退避時間、根據返回結果重試等,這裏推薦幾個第三方庫:session
本文翻譯自
Never Give Up, Retry: How Software Should Deal with Failures