我使用的是api,有时它会提供一些奇怪的状态代码,只需重试同一请求即可解决。我正在使用aiohttp异步向此api发出请求
我还使用退避库重试请求,但是似乎仍未重试401个请求。
@backoff.on_exception(backoff.expo, aiohttp.ClientError, max_tries=11, max_time=60)
async def get_user_timeline(self, session, user_id, count, max_id, trim_user, include_rts, tweet_mode):
params = {
'user_id': user_id,
'trim_user': trim_user,
'include_rts': include_rts,
'tweet_mode': tweet_mode,
'count': count
}
if (max_id and max_id != -1):
params.update({'max_id': max_id})
headers = {
'Authorization': 'Bearer {}'.format(self.access_token)
}
users_lookup_url = "/1.1/statuses/user_timeline.json"
url = self.base_url + users_lookup_url
async with session.get(url, params=params, headers=headers) as response:
result = await response.json()
response = {
'result': result,
'status': response.status,
'headers': response.headers
}
return response
如果响应的状态码不是200或429,我希望所有请求最多退回10次。
答案 0 :(得分:2)
默认情况下,aiohttp不会针对非200状态引发异常。您应该通过raise_for_status=True
(doc)来更改它:
async with session.get(url, params=params, headers=headers, raise_for_status=True) as response:
对于任何状态为400或更高的状态,它都应该引发异常,从而触发backoff
。
由于这些aren't errors,不应重试代码2xx。
无论如何,如果您仍想加注“ 200或429”以外的筹码,则可以手动进行:
if response.status not in (200, 429,):
raise aiohttp.ClientResponseError()
答案 1 :(得分:1)
我制作了一个简单的库,可以为您提供帮助:
https://github.com/inyutin/aiohttp_retry
这样的代码应该可以解决您的问题:
from aiohttp import ClientSession
from aiohttp_retry import RetryClient
statuses = {x for x in range(100, 600)}
statuses.remove(200)
statuses.remove(429)
async with ClientSession() as client:
retry_client = RetryClient(client)
async with retry_client.get("https://google.com", retry_attempts=10, retry_for_statuses=statuses) as response:
text = await response.text()
print(text)
await retry_client.close()
相反,google.com
使用您自己的url
答案 2 :(得分:0)
也许它已经太老了,但是对于任何想知道如何构建这样的解决方案的人来说
RequestData和ErrorResponseData是您的自定义类,它不是内置的
class DataAPI:
def __init__(self, api_data_converter: APIDataConverter):
self.api_data_converter = api_data_converter
async def _bound_fetch(self, request_data: RequestData, session):
try:
async with session.get(request_data.url, raise_for_status=True) as response:
return ResponseData(await response.text())
except aiohttp.ClientConnectionError as e:
Logging.log_exception('Connection error: {}'.format(str(e)))
return ErrorResponseData(url=request_data.url, request_data=request_data)
except Exception as e:
Logging.log_exception('Data API error: {}'.format(str(e)))
return ErrorResponseData(url=request_data.url, request_data=request_data)
async def _run_requests(self, request_data: List[RequestData]):
for rd in request_data:
Logging.log_info('Request: {}'.format(rd.url))
async with aiohttp.ClientSession(timeout=ClientTimeout(total=80)) as session:
tasks = []
for rd in request_data:
task = asyncio.ensure_future(self._bound_fetch(rd, session))
tasks.append(task)
responses = asyncio.gather(*tasks)
return await responses
def get_data(self, request_data: List[RequestData]):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
skipped = request_data
responses: List[ResponseData] = []
for _ in range(2): # specify your retry count instead of 2
interm_responses = loop.run_until_complete(asyncio.ensure_future(self._run_requests(skipped)))
skipped = []
for resp in interm_responses:
if isinstance(resp, ErrorResponseData):
skipped.append(resp.request_data)
else:
responses.append(resp)
if not skipped:
break
if skipped:
Logging.log_critical('Failed urls remaining')
for resp in responses:
data = self.api_data_converter.convert(resp.response)
if not data:
Logging.log_exception('Data API error')
dt = dateutil.parser.parse(data[-1]['dt'])
resp.response = data
resp.last_candle_dt = dt
return responses