运行 线程中的 flask-socketio
Running flask-socketio in thread
我有一个交互式 Python 应用程序,我还想在其中使用 flask-socketio 与 Javascript 客户端进行交互。
因此,我需要 Python socketio 到 运行 作为线程。
方法 #1:
def socketio_server_fn():
socketio.run(flask_app, port=5000)
flask_app = Flask(__name__)
socketio = flask_socketio.SocketIO(flask_app, always_connect=True, async_mode='threading')
socketio_thread = socketio.start_background_task(socketio_server_fn)
问题 1.1:它不使用 Websocket 传输,因为它与标准 Python 线程不兼容。相反,socketio 回退到轮询。
问题 1.2:轮询不仅效率低下,而且每秒都会向控制台发送如下消息:
127.0.0.1 - - [10/Oct/2019 13:57:11] "GET /socket.io/?EIO=3&transport=polling&t=MsrXwsJ&sid=c63dfaefdbb84c688dd53bef2f6d3c77 HTTP/1.1" 200 -
我无法删除这些消息,其他消息的结果也很复杂:Disable console messages in Flask server
方法 #2:使用 eventlet
首先我们需要在程序开始时修补 Python 线程系统:
import eventlet
eventlet.monkey_patch()
然后将 SocketIO 对象创建行更改为:
socketio = flask_socketio.SocketIO(flask_app, always_connect=True, async_mode='eventlet')
现在 socketio 使用 Websocket。
问题 2.1:对于整个 Python 线程系统,我宁愿避免 monkey_patch。
问题 2.2:在拆除应用程序时,使用 eventlet 似乎会破坏 socketio.stop()。 stop() 现在即使在从 HTTP 处理程序函数调用时也会挂起。相同的拆解代码适用于 async_mode='threading' 和正常的 socketio.run()。 socketio.server.stop() 也挂起。
问题2.3:Eventlet似乎与prompt_toolkit不完全兼容:
Exception in default exception handler
Traceback (most recent call last):
File "python\lib\site-packages\prompt_toolkit\eventloop\win32.py", line 102, in _run_task
t()
File "python\lib\site-packages\prompt_toolkit\eventloop\context.py", line 116, in new_func
return func(*a, **kw)
File "python\lib\site-packages\prompt_toolkit\patch_stdout.py", line 103, in write_and_flush_in_loop
run_in_terminal(write_and_flush, in_executor=False)
File "python\lib\site-packages\prompt_toolkit\application\run_in_terminal.py", line 50, in run_in_terminal
return run_coroutine_in_terminal(async_func, render_cli_done=render_cli_done)
File "python\lib\site-packages\prompt_toolkit\application\run_in_terminal.py", line 71, in run_coroutine_in_terminal
assert app._is_running
AssertionError
方法 #3:使用 gevent
这似乎根本不起作用。
运行将 flask-socketio 作为线程看似常见的用例,难道没有一个简单的解决方案吗?
既然这个问题得到了赞成,我可以说我通过跳出框框思考解决了这个难题。我改为 aiohttp.
而不是 flask-socketio
我的解决方案是这样的(不完整的代码):
from aiohttp import web
class MyThreadedServer(Thread):
def __init__(self):
self._loop = None # The event loop for the async web server
async def _on_shutdown(self, app):
for conn in set(self.connections):
await conn.close()
def run(self):
#Runs a separate asyncio loop on this (separate) thread
self._loop = asyncio.new_event_loop()
asyncio.set_event_loop(self._loop)
self.stop_server = asyncio.Event()
self._loop.run_until_complete(self._run_app())
#Or in Python 3.7, simply:
# asyncio.run(run())
self._loop.close()
async def _run_app(self):
self.app = web.Application()
self.app.on_shutdown.append(self._on_shutdown)
runner = web.AppRunner(self.app)
await runner.setup()
site = web.TCPSite(runner, self.hostname, self.port)
await site.start()
print('Started web server on %s port %d' % (self.hostname, self.port))
await self.stop_server.wait()
print('Web server closing down')
def stop(self):
"Call from any thread"
# Overrides EventThread.stop() to change implementation
if self.stop_server:
self._loop.call_soon_threadsafe(self.stop_server.set)
我有一个交互式 Python 应用程序,我还想在其中使用 flask-socketio 与 Javascript 客户端进行交互。 因此,我需要 Python socketio 到 运行 作为线程。
方法 #1:
def socketio_server_fn():
socketio.run(flask_app, port=5000)
flask_app = Flask(__name__)
socketio = flask_socketio.SocketIO(flask_app, always_connect=True, async_mode='threading')
socketio_thread = socketio.start_background_task(socketio_server_fn)
问题 1.1:它不使用 Websocket 传输,因为它与标准 Python 线程不兼容。相反,socketio 回退到轮询。
问题 1.2:轮询不仅效率低下,而且每秒都会向控制台发送如下消息:
127.0.0.1 - - [10/Oct/2019 13:57:11] "GET /socket.io/?EIO=3&transport=polling&t=MsrXwsJ&sid=c63dfaefdbb84c688dd53bef2f6d3c77 HTTP/1.1" 200 -
我无法删除这些消息,其他消息的结果也很复杂:Disable console messages in Flask server
方法 #2:使用 eventlet
首先我们需要在程序开始时修补 Python 线程系统:
import eventlet
eventlet.monkey_patch()
然后将 SocketIO 对象创建行更改为:
socketio = flask_socketio.SocketIO(flask_app, always_connect=True, async_mode='eventlet')
现在 socketio 使用 Websocket。
问题 2.1:对于整个 Python 线程系统,我宁愿避免 monkey_patch。
问题 2.2:在拆除应用程序时,使用 eventlet 似乎会破坏 socketio.stop()。 stop() 现在即使在从 HTTP 处理程序函数调用时也会挂起。相同的拆解代码适用于 async_mode='threading' 和正常的 socketio.run()。 socketio.server.stop() 也挂起。
问题2.3:Eventlet似乎与prompt_toolkit不完全兼容:
Exception in default exception handler
Traceback (most recent call last):
File "python\lib\site-packages\prompt_toolkit\eventloop\win32.py", line 102, in _run_task
t()
File "python\lib\site-packages\prompt_toolkit\eventloop\context.py", line 116, in new_func
return func(*a, **kw)
File "python\lib\site-packages\prompt_toolkit\patch_stdout.py", line 103, in write_and_flush_in_loop
run_in_terminal(write_and_flush, in_executor=False)
File "python\lib\site-packages\prompt_toolkit\application\run_in_terminal.py", line 50, in run_in_terminal
return run_coroutine_in_terminal(async_func, render_cli_done=render_cli_done)
File "python\lib\site-packages\prompt_toolkit\application\run_in_terminal.py", line 71, in run_coroutine_in_terminal
assert app._is_running
AssertionError
方法 #3:使用 gevent 这似乎根本不起作用。
运行将 flask-socketio 作为线程看似常见的用例,难道没有一个简单的解决方案吗?
既然这个问题得到了赞成,我可以说我通过跳出框框思考解决了这个难题。我改为 aiohttp.
而不是 flask-socketio我的解决方案是这样的(不完整的代码):
from aiohttp import web
class MyThreadedServer(Thread):
def __init__(self):
self._loop = None # The event loop for the async web server
async def _on_shutdown(self, app):
for conn in set(self.connections):
await conn.close()
def run(self):
#Runs a separate asyncio loop on this (separate) thread
self._loop = asyncio.new_event_loop()
asyncio.set_event_loop(self._loop)
self.stop_server = asyncio.Event()
self._loop.run_until_complete(self._run_app())
#Or in Python 3.7, simply:
# asyncio.run(run())
self._loop.close()
async def _run_app(self):
self.app = web.Application()
self.app.on_shutdown.append(self._on_shutdown)
runner = web.AppRunner(self.app)
await runner.setup()
site = web.TCPSite(runner, self.hostname, self.port)
await site.start()
print('Started web server on %s port %d' % (self.hostname, self.port))
await self.stop_server.wait()
print('Web server closing down')
def stop(self):
"Call from any thread"
# Overrides EventThread.stop() to change implementation
if self.stop_server:
self._loop.call_soon_threadsafe(self.stop_server.set)