使用Python HTTPX遇RuntimeError:连接池关闭时仍有请求在处理
解决HTTPX异步请求出现
RuntimeError: The connection pool was closed while X HTTP requests/responses were still in-flight的问题 问题原因
你的代码存在两个核心问题触发该错误:
- 提前用
asyncio.create_task()创建任务,这些任务会脱离asyncio.gather()的直接管控。当某个任务抛出异常时,asyncio.gather()会立即终止并抛出异常,触发async with httpx.AsyncClient()的上下文退出、连接池关闭,但其他已创建的任务可能仍在运行,继续使用已关闭的连接池就会报错。 - 未处理单个请求的异常,单个请求失败会直接导致整个异步任务组崩溃,进而引发连接池提前关闭。
解决方案
- 直接传递协程给
asyncio.gather():无需提前用asyncio.create_task()包装协程,asyncio.gather()会自动调度协程执行,确保所有请求都在AsyncClient的上下文内完成后才关闭连接池。 - 添加异常处理:在单个请求的协程中捕获异常,避免单个请求失败影响整个任务组;或者在
asyncio.gather()中设置return_exceptions=True,将异常作为结果返回,待所有任务完成后统一处理。
修改后的代码示例
方式一:请求级异常处理
async def gather_players(list_of_urlCodes): async def get_json(client, link): try: response = await client.get(BASE_URL + link) response.raise_for_status() # 捕获HTTP状态码异常(如404、500) return response.json()['league']['standard']['players'] except Exception as e: print(f"请求失败: {link}, 错误: {str(e)}") return [] # 返回空列表或其他默认值,避免中断整个任务组 async with httpx.AsyncClient() as client: # 直接传递协程对象,无需create_task tasks = [get_json(client, f'/prod/v1/2022/teams/{code}/roster.json') for code in list_of_urlCodes] list_of_people = await asyncio.gather(*tasks) return list_of_people def teamRoster(list_of_urlCodes: list) -> list: list_of_personIds = asyncio.run(gather_players(list_of_urlCodes)) finalResult = [] for person_list in list_of_personIds: for person in person_list: personId = person['personId'] for player in listOfPlayers: if personId == player['personId']: finalResult.append({ "playerName": f"{player['firstName']} {player['lastName']}", "personId": player['personId'], "jersey": player['jersey'], "pos": player['pos'], "heightMeters": player['heightMeters'], "weightKilograms": player['weightKilograms'], "dateOfBirthUTC": player['dateOfBirthUTC'], "nbaDebutYear": player['nbaDebutYear'], "country": player['country'] }) return finalResult
方式二:统一处理异常(保留异常信息)
如果需要后续处理异常,可修改gather调用:
list_of_people = await asyncio.gather(*tasks, return_exceptions=True)
之后在结果处理阶段过滤或处理异常:
for result in list_of_people: if isinstance(result, Exception): # 自定义异常处理逻辑,如记录日志 continue # 正常处理返回的球员数据
额外注意事项
如果你的类方法会被高频调用,反复使用asyncio.run()会不断创建销毁事件循环,影响性能。可以考虑将类的核心逻辑改为异步,复用事件循环,避免不必要的资源消耗。
内容的提问来源于stack exchange,提问作者Tony
相关产品推荐
相关产品推荐

