类中Websocket连接重试装饰器报错:期望协程却得到Client对象
问题分析与解决
错误原因
- 装饰器未适配异步函数:你的
retry装饰器返回的是普通同步函数,但被装饰的main是异步协程函数,asyncio.run需要接收协程对象,自然会触发类型错误。 - 无限递归风险:装饰器里直接调用
self.main(),而main已经被装饰成f_retry,会导致无限递归调用。 - 阻塞事件循环:使用同步的
time.sleep会卡住asyncio事件循环,破坏异步逻辑的执行流程。 - 异常未向上传递:
main函数内部捕获异常后仅打印,没有重新抛出,装饰器无法感知连接失败,不会触发重试逻辑。
修正后的代码
import asyncio import logging import websockets def retry(fnc): async def f_retry(self): tries = 2 delay = 2 backoff = 2 while tries > 0: try: # 调用原函数而非self.main(),避免递归 return await fnc(self) except Exception as e: tries -= 1 if tries <= 0: # 最后一次失败直接抛出异常 raise msg = f"{str(e)}, Retrying in {delay} seconds..." print(msg) # 使用异步sleep,不阻塞事件循环 await asyncio.sleep(delay) delay *= backoff return f_retry class Client(): def __init__(self): self.name = None @retry async def main(self): async with websockets.connect('ws://localhost:8000') as ws: logging.info("Connected to the server") # 保持连接不退出,可替换为实际业务逻辑 await asyncio.Future() if __name__ == '__main__': logging.basicConfig(level=logging.INFO) client = Client() try: asyncio.run(client.main()) except Exception as e: print(f"最终连接失败: {e}")
关键修改点
- 将装饰器内部的
f_retry改为异步函数,用await调用原异步函数fnc(self),适配协程逻辑。 - 替换
time.sleep为asyncio.sleep,避免阻塞异步事件循环。 - 移除
main内部的异常捕获,让连接失败的异常向上传递给装饰器处理,确保重试逻辑触发。 - 调整重试次数判断,当重试耗尽时抛出异常,让主函数可以捕获最终失败的情况。
内容的提问来源于stack exchange,提问作者Humberto Adhemar
相关产品推荐
相关产品推荐

