如何为asyncio aiohttp的异步请求添加超时机制?
aiohttp异步请求的超时设置方案对比
问题背景
我目前用以下代码异步调用API获取数据:
import asyncio import pandas as pd # 补充原代码遗漏的导入 import aiohttp # 补充原代码遗漏的导入 async def get_data(session, ticker): while True: try: async with session.get(url=f'api.exec.com') as response: if response.status == 200: data = await response.json() try: df = pd.DataFrame(data) except ValueError: print("Data is not a valid float. Retrying...") else: print("Received non-200 status code. Retrying...") await asyncio.sleep(1) except Exception as e: print("Unable to get url {} due to {}. Retrying...".format(ticker, e.__class__)) await asyncio.sleep(1) async def main_loop(ticker_list): async with aiohttp.ClientSession() as session: ret = await asyncio.gather(*[get_data(session, tick) for tick in ticker_list]) return ret datas = ['x1', 'x2', 'x3', 'x4'] results = asyncio.run(main_loop(datas)) # 修正原代码调用未定义函数的问题 print(results)
之前用requests库时,可以直接通过timeout=5或timeout=(4,7)设置超时,现在想知道用aiohttp/asyncio时该怎么实现,同时我试了三种写法,想确认哪种合适:
三种超时设置方式的说明
1. 直接在session.get中传timeout=5
这种写法完全有效,aiohttp的get方法支持直接接收数值类型的timeout参数,内部会自动将其转换为aiohttp.ClientTimeout(total=5)对象,效果和requests的timeout=5完全一致,是简单总超时需求的首选写法。
修正后的完整代码:
import asyncio import pandas as pd import aiohttp async def get_data(session, ticker): while True: try: async with session.get(url=f'api.exec.com', timeout=5) as response: if response.status == 200: data = await response.json() try: df = pd.DataFrame(data) except ValueError: print("Data is not a valid float. Retrying...") else: print("Received non-200 status code. Retrying...") await asyncio.sleep(1) except Exception as e: print("Unable to get url {} due to {}. Retrying...".format(ticker, e.__class__)) await asyncio.sleep(1) async def main_loop(ticker_list): async with aiohttp.ClientSession() as session: ret = await asyncio.gather(*[get_data(session, tick) for tick in ticker_list]) return ret datas = ['x1', 'x2', 'x3', 'x4'] results = asyncio.run(main_loop(datas)) print(results)
2. 传入aiohttp.ClientTimeout对象
这种方式适合需要精细化控制超时规则的场景,对应requests中timeout=(connect_timeout, read_timeout)的写法,可以分别设置请求各阶段的超时阈值:
timeout = aiohttp.ClientTimeout( total=5, # 整个请求的总超时时间 connect=2, # 建立连接的超时时间 sock_read=3, # 读取响应数据的超时时间 sock_connect=2 # 套接字连接的超时时间 )
将这个对象传入session.get,就能精准控制请求不同阶段的超时逻辑,适合对超时规则有复杂要求的场景。
3. 在ClientSession全局设置超时
这种方式会给当前session下的所有请求设置默认超时,适合所有请求超时规则一致的场景,避免重复代码。如果某个请求需要特殊的超时规则,直接在该请求的get方法中单独设置timeout参数即可,请求级别的设置会覆盖全局的session设置。
修正后的代码:
import asyncio import pandas as pd import aiohttp async def get_data(session, ticker): while True: try: async with session.get(url=f'api.exec.com') as response: if response.status == 200: data = await response.json() try: df = pd.DataFrame(data) except ValueError: print("Data is not a valid float. Retrying...") else: print("Received non-200 status code. Retrying...") await asyncio.sleep(1) except Exception as e: print("Unable to get url {} due to {}. Retrying...".format(ticker, e.__class__)) await asyncio.sleep(1) async def main_loop(ticker_list): timeout = aiohttp.ClientTimeout(total=5) async with aiohttp.ClientSession(timeout=timeout) as session: ret = await asyncio.gather(*[get_data(session, tick) for tick in ticker_list]) return ret datas = ['x1', 'x2', 'x3', 'x4'] results = asyncio.run(main_loop(datas)) print(results)
总结选择建议
- 仅需简单总超时:用方式1(直接传数值)最简洁;
- 需要分阶段控制超时:用方式2(传
ClientTimeout对象); - 所有请求超时规则一致:用方式3(全局设置)减少重复代码。
内容的提问来源于stack exchange,提问作者anarchy
相关产品推荐
相关产品推荐

