retry重试常见场景及实现

当我们的代码是有访问网络相关的操作时,比如http请求或者访问远程数据库,经常可能会发生一些错误,有些错误可能重新去发送请求就会成功,本文分析常见可能需要重试的场景,并最后给出python代码实现。

常见异常分成两种,一种是请求传输过程出错,另一种是服务端负载过高导致错误。
  对于第一种错误,可能请求还未到服务端处理程序就已经返回。
  HTTP请求错误:

  • DNSError:域名不能解析出ip地址,可能是服务端重新部署到其它地方。
  • ConnectionError:请求建立握手连接的过程出错,可能是请求时的网络质量比较差。

访问数据库错误:

  • OperationalError:与数据库服务器的连接丢失或连接失败时。比如访问PostgreSQL返回码
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

  • ProtocolError:属于Redis中的一种常见错误, 当Redis服务器收到一个字节序列并转换为无意义的操作时,会引发异常。由于您在部署之前测试了软件,因此编写错误的代码不太可能发生错误。可能是传输层出现了错误。

对于第二类错误,服务器负载过高导致。对于HTTP请求,可根据状态码识别:

  •   408 Request Timeout: 当服务器花费很多时间处理您的请求而不能在等待时间返回。可能的原因:资源被大量传入请求所淹没。一段时间后等待和重试可能是最终完成数据处理的好策略。
  • 429 Too Many Requests: 在一段时间内发送的请求数量超过服务器允许的数量。服务器使用的这种技术称为速率限制。良好的服务端应该返回Retry-After标头,它提供建议在下一个请求之前需要等待多长时间。
  • 500 Internal Server Error: 这是最臭名昭着的HTTP服务器错误。错误原因多样性,对于发生的所有未捕获的异常,都返回这种错误。对于这种错误,应了解背后的原因再决定是否重试。
  • 503 Service Unavailable:由于临时过载,服务当前无法处理请求。经过一段时间的推迟,能得到缓解。
  • 504 Gateway Timeout:类似于408请求超时,网关或反向代理不能及时从上游服务器得到响应。

对于数据库访问:

  • OperationalError. 对于PostgreSQL和MySQL,它还包括不受软件工程师控制的故障。例如:处理期间发生内存分配错误,或无法处理事务。我建议重试它们。
  • IntegrityError: 当违反外键约束时可以引发它,例如当您尝试插入依赖于记录B的记录A时。由于系统的异步性质,可能还没有添加记录B.在这种情况下,进行重试。另一方面,当您尝试添加记录导致重复唯一键时,也会引发这种异常,这种情况下不需要重试。那么如何去识别这种情况,DBMS能返回状态码,假如mysql驱动能在状态码和异常类之间映射,就能识别这种需要重试的场景,在python3中,库pymysql可以在数据库返回码和异常之间映射。地址如下:

constants for MySQL errors
      the mapping between exception types in PyMYSQL and error codes.

本文以网络IO为例,利用python装饰器实现重试机制。用fetch函数去发送http请求下载网页

# Example is taken from http://aiohttp.readthedocs.io/en/stable/#getting-started
import aiohttp
import asyncioasync 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 loggingfrom functools import wrapslog = 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 executionretries (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 = 0while True:try:result = await func(*args, **kwargs)except exceptions as err:retries_count += 1message = "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 errelse:verbose and log.warning(message)if cooldown:await asyncio.sleep(cooldown)else:return resultreturn innerreturn wrap

基本思想是在达到重试次数限制之前捕获预期的异常。在每次执行之间,等待固定时间。此外,如果我们想要详细,会写每个失败尝试的日志。当然,本例子只提供了几个重试选项,一个完备的重试库应该提供更多重试配置,比如指数退避时间、根据返回结果重试等,这里推荐几个第三方库:

  • tenacity
  • retrying
  • backoff
  • riprova

本文翻译自

Never Give Up, Retry: How Software Should Deal with Failures

下一篇博文将通过分析retrying源码来深入分析重试机制的实现原理。

posted on 2018-10-20 16:43 killianxu 阅读(...) 评论(...) 编辑 收藏

retry重试常见场景及实现相关推荐

  1. foxmail邮件加载失败重试_java retry(重试) spring retry, guava retrying 详解

    系列说明 java retry 的一步步实现机制. java-retry 源码地址 情景导入 简单的需求 产品经理:实现一个按条件,查询用户信息的服务. 小明:好的.没问题. 代码 UserServi ...

  2. java retry(重试) spring retry, guava retrying 详解

    转载 自 http://blog.51cto.com/9250070/2156431 系列说明 java retry 的一步步实现机制. java-retry 源码地址 情景导入 简单的需求 产品经理 ...

  3. 雪崩效应及其常见场景和解决方案

    一.什么是雪崩效应? 在分布式系统架构中,多个系统之间通常是通过远程 RPC 调用进行通信,也就是 A 系统调用 B 系统服务,B 系统调用 C 系统服务等(实现方式有 Spring Boot + D ...

  4. bisect git 使用_Git使用过程中的一些常见场景问题总结

    之前在公司内部推Git,写了一份git使用教程,后来又在团队内部做了一次分享,内容是关于Git使用过程中经常会遇到的一些场景,并有了这份总结. git基础 基于feature的工作流 添加忽略文件 . ...

  5. iOS 内存泄漏的常见场景

    内存泄漏的常见场景 CF类型内存 注意以create,copy作为关键字的函数都是需要释放内存的,注意配对使用.比如:CGColorCreate<-->CGColorRelease MRC ...

  6. 前端加密的常见场景和方法

    前端加密的常见场景和方法 首先,加密的目的,简而言之就是将明文转换为密文.甚至转换为其他的东西, 用来隐藏明文内容本身,防止其他人直接获取到敏感明文信息.或者提高其他 人获取到明文信息的难度.通常我们 ...

  7. 蓝牙室内定位UWB常见场景定位分析

    在应用市场需求下,蓝牙室内定位&UWB"多技术融合"成为定位技术发展的必然趋势.在现在没有哪一种单独的定位技术可以满足一个定位场景里面的所有定位需求,不同区域里面最实用的定 ...

  8. disruptor笔记之六:常见场景

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos <disruptor笔记>系列链接 快速入 ...

  9. Android开发者必看:Git使用过程中的一些常见场景问题总结

    10. 加入过历史版本的文件,因某些原因被删除了想恢复 git checkout <commit_id> – <file_name> 另外你也可以用reset命令来完成 11. ...

最新文章

  1. 【Android 逆向】Android 进程代码注入原理 ( 进程注入原理 | 远程调用流程 | 获取函数地址 | 设置 IP 寄存器 | mmap 申请内存 | 设置 SP 寄存器 )
  2. About mac80211
  3. 浅谈代码的执行效率(3):缓存与局部性
  4. Django 序列化三种方式 对象 列表 元组
  5. activiti搭建
  6. spring的IOC注解
  7. 日期格式不符合要求:Unparseable date: quot;3e8a4d83533744c698216535a65850c0quot;
  8. 大顶堆删除最大值_C++|使用STL算法创建、调整、输出最大堆、最小堆
  9. 关于集合中元素的有序无序的易混淆点
  10. python安装jupyter出现问题_有关jupyterhub安装的问题
  11. 36 岁清华 IT 男,死前对妈妈说:我好累
  12. 集群介绍 keepalived介绍 用keepalived配置高可用集群
  13. linux下ssh安装教程,linux安装ssh
  14. 135、137、138、139和445端口
  15. 台式计算机没有声音怎么办,台式机没有声音怎么办_台式机声音修复方法-太平洋IT百科...
  16. JAVA个人博客系统毕业设计,个人博客系统设计与实现,个人博客网页设计毕设作品
  17. 【计算机毕业设计】248高校奖学金管理系统
  18. dsm操作系统服务器,DSM 5.1操作系统提供以下新功能
  19. wii游戏wuyou_如何备份,交换和更新Wii游戏的保存内容
  20. Solidworks2008 API 开发的问题。

热门文章

  1. js获取最大整数的方法
  2. 填坑—c语言写单片机中断程序无法返回到中断点—解决办法
  3. 正则表达式学习——(2)正则回溯
  4. 【每日学习】深度学习相关知识
  5. Python class __int__容易理解
  6. 一、YouTube-8M 初探(视频与音频分类)
  7. 主机链接无线网虚拟机nat模式固定ip设置
  8. chrome浏览器本地信息的获取与设置
  9. 安腾处理器 oracle,英特尔展示下一代安腾处理器Poulson
  10. MCAL中MCU的配置