Python 从Starlete中的同步迭代器通过websocket发送数据

Python 从Starlete中的同步迭代器通过websocket发送数据,python,websocket,python-asyncio,starlette,Python,Websocket,Python Asyncio,Starlette,我有一个同步迭代器,它来自第三方软件包。迭代器查询外部服务并生成一些数据。如果没有数据,迭代器将等待它。我将Starlette中的WebSocketEndpoint子类化,以通过websocket从迭代器发送新数据。不幸的是,我似乎不理解一些东西,我的代码也没有按预期工作。下面是一个稍微简化的代码: 导入时间 从starlette.endpoints导入WebSocketEndpoint 从starlette.websockets导入WebSocket 类迭代器: “”“这是第三方对象,根本不是

我有一个同步迭代器,它来自第三方软件包。迭代器查询外部服务并生成一些数据。如果没有数据,迭代器将等待它。我将Starlette中的
WebSocketEndpoint
子类化,以通过websocket从迭代器发送新数据。不幸的是,我似乎不理解一些东西,我的代码也没有按预期工作。下面是一个稍微简化的代码:

导入时间
从starlette.endpoints导入WebSocketEndpoint
从starlette.websockets导入WebSocket
类迭代器:
“”“这是第三方对象,根本不是异步的。”“”
定义初始化(自):
self.\u stop=False
定义(自我):
self.i=0
回归自我
定义下一个(自我):
如果自行停止:
提出停止迭代
时间。睡眠(5)
self.i+=1
打印(self.i)
回归自我
def取消(自我):
self.\u stop=True
类MyWebSocket(WebSocketEndpoint):
定义初始化(自我、作用域、接收、发送)->无:
super().\uuuu init\uuuuu(作用域、接收、发送)
self.iterator=迭代器()
_connect上的异步定义(self,websocket:websocket)->无:
在连接(websocket)上等待super()
对于self.iterator中的消息:
等待websocket.send_json({“message”:message})
断开连接时的异步定义(self,websocket:websocket,close_code:int)->无:
等待super()。断开连接(websocket,关闭代码)
self.iterator.cancel()
第一个问题-代码没有通过websocket发送任何数据。print语句表明迭代器生成数据,但实际上没有发送任何数据。如果我将
return
放在
websocket.sendu json()
之后,它将正确地从迭代器发送第一个结果,但循环将在之后完成。为什么?

另一个问题是迭代器完全阻塞了应用程序的执行。我理解为什么会发生这种情况,但由于它是一个web服务,迭代器将一直工作,直到客户机断开与websocket的连接,因此它可以很容易地阻止我的整个应用程序。如果我有10个worker,那么10个websocket客户端将阻止该应用程序,并且在其中一个websocket断开连接之前无法执行任何操作。我如何解决它

这是一个第三方对象,根本不是异步的

这就是问题所在——asyncio是单线程的,因此迭代器必须完全不阻塞(就像在内置集合上迭代时),或者必须使用And和
async for
循环,该循环将在等待下一项时暂停其执行

在处理第三方阻塞函数时,您可以将其合并到异步代码中,使用异步代码将函数提交到线程池,并挂起当前协同程序,直到函数完成。您不能将迭代器直接传递给
run\u-in\u-executor
,但可以创建一个包装器,该包装器接受一个同步迭代器,并通过
run\u-in\u-executor
运行
的每个调用,从而提供异步迭代器的接口。例如:

async def wrap_iter(iterable):
    loop = asyncio.get_event_loop()
    it = iter(iterable)

    DONE = object()
    def get_next_item():
        # Get the next item synchronously.  We cannot call next(it)
        # directly because StopIteration cannot be transferred
        # across an "await".  So we detect StopIteration and
        # convert it to a sentinel object.
        try:
            return next(it)
        except StopIteration:
            return DONE

    while True:
        # Submit execution of next(it) to another thread and resume
        # when it's done.  await will suspend the coroutine and
        # allow other tasks to execute while waiting.
        next_item = await loop.run_in_executor(None, get_next_item)
        if next_item is DONE:
            break
        yield next_item

现在您可以将self.iterator中的
消息替换为wrap_iter(self.iterator)
中的
消息异步,一切都应该正常。

谢谢,我错过了
异步for
的存在。