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