You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

类中Websocket连接重试装饰器报错:期望协程却得到Client对象

问题分析与解决

错误原因

  1. 装饰器未适配异步函数:你的retry装饰器返回的是普通同步函数,但被装饰的main是异步协程函数,asyncio.run需要接收协程对象,自然会触发类型错误。
  2. 无限递归风险:装饰器里直接调用self.main(),而main已经被装饰成f_retry,会导致无限递归调用。
  3. 阻塞事件循环:使用同步的time.sleep会卡住asyncio事件循环,破坏异步逻辑的执行流程。
  4. 异常未向上传递: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 11:42:24