当咱们的代码是有访问网络相关的操做时,好比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