Python-SocketIO 服务器需要很长时间才能触发事件
Python-SocketIO server takes a long time to fire event
我有一个 python websocket 服务器试图与 javascript websocket 客户端(嵌入在 HTML 中)通信。事件会立即从服务器 emit
编辑,但服务器需要超过 30 秒才能发送事件触发器,尽管客户端和服务器都在本地托管。
服务器相关代码如下:
sio = socketio.AsyncServer(cors_allowed_origins='*')
app = web.Application() #aiohttp web server
loop = asyncio.get_event_loop()
sio.attach(app)
async def index(request):
with open('./index.html') as f:
return web.Response(text=f.read(), content_type='text/html')
app.router.add_get('/', index)
app.router.add_get('/index.html', index)
if __name__ == '__main__':
web.run_app(app)
事件像这样被触发(编辑,这必须通过事件循环来完成,因为 emit
是一个异步函数,运行 来自同步函数。):
print('Starting event')
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(sio.emit('ChangeProgressState'))
loop.close()
print('Event has been fired.')
但是,打印语句会立即出现。在客户端,我正在连接并尝试使用这样的事件:
const socket = io.connect("http://localhost:8080", {
transports: ['websocket']
})
socket.on("ChangeProgressState", function (data) {
console.log("got event.")
//some code here...
});
但是,从事件触发到 javascript 套接字注意到的时间可能会很长,从 30 秒到有时几分钟。我这里有什么地方做错了吗?
应该注意,消耗的资源非常少 (2%-5%)(包括内存和 CPU),所以我目前认为这不是问题所在。任何帮助将非常感激。
编辑 11/15/2019:我尝试查看应用程序的网络选项卡(raspberry pi 上的 chromium-browser)。它似乎显示了初始套接字连接,但它没有显示套接字之间的通信方面的任何内容,即使在事件最终触发之后也是如此。
编辑 2:这显然是服务器端的问题。我基本上可以立即将事件从 JS 客户端发送到 python 服务器,但是在另一个方向上需要很长时间才能到达。我不太清楚为什么。
啊好的,所以我的直觉说这听起来像是客户端在进行长轮询。很多socket库都是先建立long-polling,然后升级到ws连接。
看完后Socket.io:
... which first establishes a long-polling connection, then tries to upgrade to better transports that are “tested” on the side, like WebSocket. ...
所以我不相信你做错了什么,这只是建立WebSocket连接的初始化过程。
至于 python 部分,老实说,这对我来说有点模糊。我的第一个猜测是循环代码不会阻止打印语句的执行——但我对 JavaScript 比 Python 更熟悉,所以在这方面并不完全确定。我的第二个猜测是,我确实从其他 pub/sub 库中了解到,服务器端引擎有时会使用某种中间层(有时是缓存,有时是队列)来帮助确保消息是 sent/received,也有这个可能。
额外花絮:我怀疑如果您查看浏览器开发工具的网络选项卡,它会显示该行为,某种形式的 HTTP 请求,然后您最终会看到套接字连接。转动你的 Python server/service off/on 也会证明 socket.io 在浏览器和边缘情况下的稳健性,因为它在与各种互联网通信时如何处理不稳定的网络通信协议。
感谢所有帮助回答这个问题的人!我终于找到了一个有点非正统的解决方案,所以我在这里解释一下整个情况。
本质上,为了 运行 同步上下文中的异步方法,您必须在事件循环中使用 asyncio 的 run_until_complete
方法。当被问到这个问题时,我就是这样做的。但是,在与 python-socketio 库的创建者交谈后,您似乎必须 运行 在与服务器 运行 正在进入的事件循环相同的事件循环中。
但是,这会产生不同的问题。如果一个事件循环已经是 运行ning,python 不允许你在它上面使用 run_until_complete,给你一个错误:RuntimeError: This event loop is already running.
所以,这听起来很矛盾吧?你是对的。然而,这个问题非常普遍,以至于存在另一个库,其唯一目的是猴子修补 python asyncio 库来解决这个问题。我找到了这个图书馆 here .
安装并使用该库后,我现在可以这样做,这完全解决了我的问题:
main_event_loop = asyncio.get_event_loop()
main_event_loop.run_until_complete(sio.emit("ChangeProgressState"))
现在程序 运行 符合预期,消息立即 sent/arriving。
我有一个 python websocket 服务器试图与 javascript websocket 客户端(嵌入在 HTML 中)通信。事件会立即从服务器 emit
编辑,但服务器需要超过 30 秒才能发送事件触发器,尽管客户端和服务器都在本地托管。
服务器相关代码如下:
sio = socketio.AsyncServer(cors_allowed_origins='*')
app = web.Application() #aiohttp web server
loop = asyncio.get_event_loop()
sio.attach(app)
async def index(request):
with open('./index.html') as f:
return web.Response(text=f.read(), content_type='text/html')
app.router.add_get('/', index)
app.router.add_get('/index.html', index)
if __name__ == '__main__':
web.run_app(app)
事件像这样被触发(编辑,这必须通过事件循环来完成,因为 emit
是一个异步函数,运行 来自同步函数。):
print('Starting event')
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(sio.emit('ChangeProgressState'))
loop.close()
print('Event has been fired.')
但是,打印语句会立即出现。在客户端,我正在连接并尝试使用这样的事件:
const socket = io.connect("http://localhost:8080", {
transports: ['websocket']
})
socket.on("ChangeProgressState", function (data) {
console.log("got event.")
//some code here...
});
但是,从事件触发到 javascript 套接字注意到的时间可能会很长,从 30 秒到有时几分钟。我这里有什么地方做错了吗?
应该注意,消耗的资源非常少 (2%-5%)(包括内存和 CPU),所以我目前认为这不是问题所在。任何帮助将非常感激。
编辑 11/15/2019:我尝试查看应用程序的网络选项卡(raspberry pi 上的 chromium-browser)。它似乎显示了初始套接字连接,但它没有显示套接字之间的通信方面的任何内容,即使在事件最终触发之后也是如此。
编辑 2:这显然是服务器端的问题。我基本上可以立即将事件从 JS 客户端发送到 python 服务器,但是在另一个方向上需要很长时间才能到达。我不太清楚为什么。
啊好的,所以我的直觉说这听起来像是客户端在进行长轮询。很多socket库都是先建立long-polling,然后升级到ws连接。
看完后Socket.io:
... which first establishes a long-polling connection, then tries to upgrade to better transports that are “tested” on the side, like WebSocket. ...
所以我不相信你做错了什么,这只是建立WebSocket连接的初始化过程。
至于 python 部分,老实说,这对我来说有点模糊。我的第一个猜测是循环代码不会阻止打印语句的执行——但我对 JavaScript 比 Python 更熟悉,所以在这方面并不完全确定。我的第二个猜测是,我确实从其他 pub/sub 库中了解到,服务器端引擎有时会使用某种中间层(有时是缓存,有时是队列)来帮助确保消息是 sent/received,也有这个可能。
额外花絮:我怀疑如果您查看浏览器开发工具的网络选项卡,它会显示该行为,某种形式的 HTTP 请求,然后您最终会看到套接字连接。转动你的 Python server/service off/on 也会证明 socket.io 在浏览器和边缘情况下的稳健性,因为它在与各种互联网通信时如何处理不稳定的网络通信协议。
感谢所有帮助回答这个问题的人!我终于找到了一个有点非正统的解决方案,所以我在这里解释一下整个情况。
本质上,为了 运行 同步上下文中的异步方法,您必须在事件循环中使用 asyncio 的 run_until_complete
方法。当被问到这个问题时,我就是这样做的。但是,在与 python-socketio 库的创建者交谈后,您似乎必须 运行 在与服务器 运行 正在进入的事件循环相同的事件循环中。
但是,这会产生不同的问题。如果一个事件循环已经是 运行ning,python 不允许你在它上面使用 run_until_complete,给你一个错误:RuntimeError: This event loop is already running.
所以,这听起来很矛盾吧?你是对的。然而,这个问题非常普遍,以至于存在另一个库,其唯一目的是猴子修补 python asyncio 库来解决这个问题。我找到了这个图书馆 here .
安装并使用该库后,我现在可以这样做,这完全解决了我的问题:
main_event_loop = asyncio.get_event_loop()
main_event_loop.run_until_complete(sio.emit("ChangeProgressState"))
现在程序 运行 符合预期,消息立即 sent/arriving。