如何在python请求库中实现重试机制?
我想在python请求库中添加一个重试机制,这样使用它的脚本将重试非致命错误 现在我考虑三种错误是可恢复的:如何在python请求库中实现重试机制?,python,http,python-requests,Python,Http,Python Requests,我想在python请求库中添加一个重试机制,这样使用它的脚本将重试非致命错误 现在我考虑三种错误是可恢复的: HTTP返回代码502、503、504 找不到主机(现在不太重要) 请求超时 在第一阶段,我确实希望每分钟重试指定的5xx请求 我希望能够透明地添加此功能,而不必手动实现从这些使用python请求的脚本或库中进行的每个HTTP调用的恢复。这是我用于重试使用urllib2进行请求的代码片段。也许您可以将其用于您的目的: retries = 1 success = False while
- HTTP返回代码502、503、504
- 找不到主机(现在不太重要)
- 请求超时
我希望能够透明地添加此功能,而不必手动实现从这些使用python请求的脚本或库中进行的每个HTTP调用的恢复。这是我用于重试使用urllib2进行请求的代码片段。也许您可以将其用于您的目的:
retries = 1
success = False
while not success:
try:
response = urllib2.urlopen(request)
success = True
except Exception as e:
wait = retries * 30;
print 'Error! Waiting %s secs and re-trying...' % wait
sys.stdout.flush()
time.sleep(wait)
retries += 1
等待时间会逐渐增加,以避免被禁止进入服务器。通过扩展
请求。会话
类,我能够获得所需的可靠性级别
这是密码
编辑该代码是:
from requests import Session
from requests.exceptions import ConnectionError
import logging
import time
class ResilientSession(Session):
"""
This class is supposed to retry requests that do return temporary errors.
At this moment it supports: 502, 503, 504
"""
def __recoverable(self, error, url, request, counter=1):
if hasattr(error,'status_code'):
if error.status_code in [502, 503, 504]:
error = "HTTP %s" % error.status_code
else:
return False
DELAY = 10 * counter
logging.warn("Got recoverable error [%s] from %s %s, retry #%s in %ss" % (error, request, url, counter, DELAY))
time.sleep(DELAY)
return True
def get(self, url, **kwargs):
counter = 0
while True:
counter += 1
try:
r = super(ResilientSession, self).get(url, **kwargs)
except ConnectionError as e:
r = e.message
if self.__recoverable(r, url, 'GET', counter):
continue
return r
def post(self, url, **kwargs):
counter = 0
while True:
counter += 1
try:
r = super(ResilientSession, self).post(url, **kwargs)
except ConnectionError as e:
r = e.message
if self.__recoverable(r, url, 'POST', counter):
continue
return r
def delete(self, url, **kwargs):
counter = 0
while True:
counter += 1
try:
r = super(ResilientSession, self).delete(url, **kwargs)
except ConnectionError as e:
r = e.message
if self.__recoverable(r, url, 'DELETE', counter):
continue
return r
def put(self, url, **kwargs):
counter = 0
while True:
counter += 1
try:
r = super(ResilientSession, self).put(url, **kwargs)
except ConnectionError as e:
r = e.message
if self.__recoverable(r, url, 'PUT', counter):
continue
return r
def head(self, url, **kwargs):
counter = 0
while True:
counter += 1
try:
r = super(ResilientSession, self).head(url, **kwargs)
except ConnectionError as e:
r = e.message
if self.__recoverable(r, url, 'HEAD', counter):
continue
return r
def patch(self, url, **kwargs):
counter = 0
while True:
counter += 1
try:
r = super(ResilientSession, self).patch(url, **kwargs)
except ConnectionError as e:
r = e.message
if self.__recoverable(r, url, 'PATCH', counter):
continue
return r
def options(self, url, **kwargs):
counter = 0
while True:
counter += 1
try:
r = super(ResilientSession, self).options(url, **kwargs)
except ConnectionError as e:
r = e.message
if self.__recoverable(r, url, 'OPTIONS', counter):
continue
return r
这段代码将使来自同一会话的所有HTTP请求总共重试5次,在两次重试之间休眠,并增加0、2、4、8、16秒的退避(第一次重试立即完成)。它将重试基本连接问题(包括DNS查找失败)和HTTP状态代码502、503和504
import logging
import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
logging.basicConfig(level=logging.DEBUG)
s = requests.Session()
retries = Retry(total=5, backoff_factor=1, status_forcelist=[ 502, 503, 504 ])
s.mount('http://', HTTPAdapter(max_retries=retries))
s.get("http://httpstat.us/503")
有关详细信息,请参阅。使用
如果在t1=1秒、t2=2秒、t3=4秒的时间间隔内发生异常,则重试某些逻辑的方法。 我们也可以增加/减少时间间隔
MAX_RETRY = 3
retries = 0
try:
call_to_api() // some business logic goes here.
except Exception as exception:
retries += 1
if retries <= MAX_RETRY:
print("ERROR=Method failed. Retrying ... #%s", retries)
time.sleep((1 << retries) * 1) // retry happens after time as a exponent of 2
continue
else:
raise Exception(exception)
MAX\u RETRY=3
重试次数=0
尝试:
调用\到\ api()//这里有一些业务逻辑。
例外情况除外:
重试次数+=1
如果重试确实有帮助?@thefourtheye:这只适用于传输级错误;套接字超时、SSL错误等。不包括500范围内的服务器返回代码。python请求是否处理状态代码429?遗憾的是,大多数网站在限制速率时发送不适当的代码(如503和404)。请求包括urllib3重试类的副本(在requests.packages.util.Retry.Retry中),这将允许细粒度控制,并包括用于重试的退避机制。对于基于状态的重试,请使用参数:status\u forcelist,该参数将强制根据所选策略重试特定的状态代码响应。@datashaman I已经实现了所谓的ResilientSession,它以透明的方式处理此问题。看看我在JIRA Python库中的实现,你应该把代码复制粘贴到这里。你不应该用非现场的链接来回答问题。另外,TBH,你的代码可能要短得多。我认为将它重组为一个decorator,您将拥有一个更短、更少重复的解决方案。仍然有一些重复(所有方法都有相同的模式),但它的工作原理完全相同,您只需在一个地方更改逻辑:一个更简洁的版本,对请求方法进行重试,删除代码中的所有重复:我知道这段代码已经有5年的历史了,但它的质量非常低。@datashaman的答案应该被接受,这样这个答案就不会受到太多的关注。你的答案是一个简单的try-except公式,没有调用不同的库和函数。我宁愿在循环中显式使用“continue”,默认情况下使用break。只是为了避免错误的无限循环。为了限制重试次数,我可以在不成功时使用,重试次数<5:
根据文档,第一次退避是0,而不是1。所以它就像0s,2s,4s,8s,16s session.mount的第一个参数的意义是什么,http://
?它有什么用途?@JamesWierzba它是一种连接到适配器的模式。因此,在本例中,所有以http://
开头的URL都将使用该适配器。您可以为https://cnn.com
例如。当访问https://cnn.com
和其他所有方法的默认值。在Retry类中查找方法\u白名单
选项,默认情况下,除了['HEAD','TRACE','GET','PUT','OPTIONS','DELETE',它不会在http方法上重试
-这些方法预计不会有副作用set method_whitelist=False也会重试API后调用。例如,如果您正在与GraphQL端点通信,则所有调用都是POST。默认情况下,只重试['HEAD','TRACE','GET','PUT','OPTIONS','DELETE'],因为它们被视为安全/幂等操作。是否需要将中的self.session.GET
调用\u-to\u api()
包装在try块中?
MAX_RETRY = 3
retries = 0
try:
call_to_api() // some business logic goes here.
except Exception as exception:
retries += 1
if retries <= MAX_RETRY:
print("ERROR=Method failed. Retrying ... #%s", retries)
time.sleep((1 << retries) * 1) // retry happens after time as a exponent of 2
continue
else:
raise Exception(exception)
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
MAX_RETRY = 2
MAX_RETRY_FOR_SESSION = 2
BACK_OFF_FACTOR = 0.3
TIME_BETWEEN_RETRIES = 1000
ERROR_CODES = (500, 502, 504)
def requests_retry_session(retries=MAX_RETRY_FOR_SESSION,
back_off_factor=BACK_OFF_FACTOR,
status_force_list=ERROR_CODES,
session=None):
session = session
retry = Retry(total=retries, read=retries, connect=retries,
backoff_factor=back_off_factor,
status_forcelist=status_force_list,
method_whitelist=frozenset(['GET', 'POST']))
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
return session
class ConfigService:
def __init__(self):
self.session = requests_retry_session(session=requests.Session())
def call_to_api():
config_url = 'http://localhost:8080/predict/'
headers = {
"Content-Type": "application/json",
"x-api-key": self.x_api_key
}
response = self.session.get(config_url, headers=headers)
return response