运行 辅助线程上的任何 Web 服务器事件循环
Running any web server event loop on a secondary thread
我们有一个丰富的后端应用程序来处理 messaging/queuing、数据库查询和计算机视觉。我们需要的另一个功能是 tcp 通信——最好是通过 http。重点是:这不是主要 一个网络应用程序。我们希望为不同的目的设置一组 http 通道。是的 - 我们了解包括主题和发布-订阅在内的消息传递:但是直接基于 tcp request/response 也有它的位置。
我查看并试用了六个 python http 网络服务器。它们隐式或显式地描述了对 main 线程上的 运行 和 event loop
的要求。这对我们来说是本末倒置:main
线程已经被其他任务占用,包括协调其他活动。
为了说明预期的结构,我将从我的 aiohttp
特定问题 中提取代码。在那个问题中,我在另一个独立脚本中尝试 运行ning,但在从属线程上:
def runWebapp():
from aiohttp import web
async def handle(request):
name = request.match_info.get('name', "Anonymous")
text = "Hello, " + name
return web.Response(text=text)
app = web.Application()
app.add_routes([web.get('/', handle),
web.get('/{name}', handle)])
web.run_app(app)
if __name__ == '__main__':
from threading import Thread
t = Thread(target=runWebapp)
t.start()
print('thread started let''s nap..')
import time
time.sleep(50)
这给出了错误:
RuntimeError: There is no current event loop in thread 'Thread-1'.
这个错误的意思是"hey you're not running this on the main
thread"。
我们可以在逻辑上用这里的其他网络服务器替换aiohttp
。这种在辅助线程上请求 Web 服务器的事件处理循环到 运行 的方法是否适用?到目前为止,我还尝试了 cherrypy
、tornado
和 flask
。
请注意,我没有尝试过的一个著名网络服务器是 django
。但是那个似乎需要围绕 django
预期(/需要?)的目录结构对应用程序进行广泛的重组。我们不想这样做,因为该应用程序有一组其他目的,可以取代拥有 http 服务器的这种杂耍。
我看过的一种方法是asyncio
。我不明白它是否可以支持 运行 侧线程上的事件循环:如果是这样,那么它就是这个问题的答案。
在任何情况下,是否有任何 Web 服务器明确支持将其事件循环从主线程中分离出来?
您可以在辅助线程上创建和设置事件循环:
asyncio.set_event_loop(asyncio.new_event_loop())
cherrypy
和 flask
没有这个就已经可以工作了; tornado
适用于此。
在 aiohttp
上,你从它调用 loop.add_signal_handler()
:
得到另一个错误
ValueError: set_wakeup_fd only works in main thread
您需要跳过它,因为 only the main thread of the main interpreter is allowed to set a new signal handler,这意味着辅助线程上的 Web 服务器 运行 无法直接处理信号以正常退出。
示例:aiohttp
调用前设置事件循环run_app()
.
aiohttp
3.8+ 已经 uses a new event loop in run_app()
,所以你可以跳过这个。
调用run_app()
时传递handle_signals=False
不添加信号处理程序。
asyncio.set_event_loop(asyncio.new_event_loop()) # aiohttp<3.8
web.run_app(app, handle_signals=False)
示例:龙卷风
调用前设置事件循环app.listen()
。
asyncio.set_event_loop(asyncio.new_event_loop())
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
任何 Python 程序都是 运行 在单个线程上,即 main
。当您创建 Thread
时,并不意味着您的程序已经使用了两个线程。
不幸的是,不可能对每个 Thread
使用不同的事件循环,但可以使用 multiprocessing
而不是 threading
来做到这一点。
它允许为每个 Process
.
创建自己的事件循环
from multiprocessing import Process
from aiohttp import web
def runWebapp(port):
async def handle(request):
name = request.match_info.get("name", "Anonymous")
text = "Hello, " + name
return web.Response(text=text)
app = web.Application()
app.add_routes([
web.get("/", handle),
web.get("/{name}", handle)
])
web.run_app(app, port=port)
if __name__ == "__main__":
p1 = Process(target=runWebapp, args=(8080,))
p2 = Process(target=runWebapp, args=(8081,))
p1.start()
p2.start()
我们有一个丰富的后端应用程序来处理 messaging/queuing、数据库查询和计算机视觉。我们需要的另一个功能是 tcp 通信——最好是通过 http。重点是:这不是主要 一个网络应用程序。我们希望为不同的目的设置一组 http 通道。是的 - 我们了解包括主题和发布-订阅在内的消息传递:但是直接基于 tcp request/response 也有它的位置。
我查看并试用了六个 python http 网络服务器。它们隐式或显式地描述了对 main 线程上的 运行 和 event loop
的要求。这对我们来说是本末倒置:main
线程已经被其他任务占用,包括协调其他活动。
为了说明预期的结构,我将从我的 aiohttp
特定问题 中提取代码。在那个问题中,我在另一个独立脚本中尝试 运行ning,但在从属线程上:
def runWebapp():
from aiohttp import web
async def handle(request):
name = request.match_info.get('name', "Anonymous")
text = "Hello, " + name
return web.Response(text=text)
app = web.Application()
app.add_routes([web.get('/', handle),
web.get('/{name}', handle)])
web.run_app(app)
if __name__ == '__main__':
from threading import Thread
t = Thread(target=runWebapp)
t.start()
print('thread started let''s nap..')
import time
time.sleep(50)
这给出了错误:
RuntimeError: There is no current event loop in thread 'Thread-1'.
这个错误的意思是"hey you're not running this on the main
thread"。
我们可以在逻辑上用这里的其他网络服务器替换aiohttp
。这种在辅助线程上请求 Web 服务器的事件处理循环到 运行 的方法是否适用?到目前为止,我还尝试了 cherrypy
、tornado
和 flask
。
请注意,我没有尝试过的一个著名网络服务器是 django
。但是那个似乎需要围绕 django
预期(/需要?)的目录结构对应用程序进行广泛的重组。我们不想这样做,因为该应用程序有一组其他目的,可以取代拥有 http 服务器的这种杂耍。
我看过的一种方法是asyncio
。我不明白它是否可以支持 运行 侧线程上的事件循环:如果是这样,那么它就是这个问题的答案。
在任何情况下,是否有任何 Web 服务器明确支持将其事件循环从主线程中分离出来?
您可以在辅助线程上创建和设置事件循环:
asyncio.set_event_loop(asyncio.new_event_loop())
cherrypy
和 flask
没有这个就已经可以工作了; tornado
适用于此。
在 aiohttp
上,你从它调用 loop.add_signal_handler()
:
ValueError: set_wakeup_fd only works in main thread
您需要跳过它,因为 only the main thread of the main interpreter is allowed to set a new signal handler,这意味着辅助线程上的 Web 服务器 运行 无法直接处理信号以正常退出。
示例:aiohttp
调用前设置事件循环
run_app()
.
aiohttp
3.8+ 已经 uses a new event loop inrun_app()
,所以你可以跳过这个。调用
run_app()
时传递handle_signals=False
不添加信号处理程序。
asyncio.set_event_loop(asyncio.new_event_loop()) # aiohttp<3.8
web.run_app(app, handle_signals=False)
示例:龙卷风
调用前设置事件循环app.listen()
。
asyncio.set_event_loop(asyncio.new_event_loop())
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
任何 Python 程序都是 运行 在单个线程上,即 main
。当您创建 Thread
时,并不意味着您的程序已经使用了两个线程。
不幸的是,不可能对每个 Thread
使用不同的事件循环,但可以使用 multiprocessing
而不是 threading
来做到这一点。
它允许为每个 Process
.
from multiprocessing import Process
from aiohttp import web
def runWebapp(port):
async def handle(request):
name = request.match_info.get("name", "Anonymous")
text = "Hello, " + name
return web.Response(text=text)
app = web.Application()
app.add_routes([
web.get("/", handle),
web.get("/{name}", handle)
])
web.run_app(app, port=port)
if __name__ == "__main__":
p1 = Process(target=runWebapp, args=(8080,))
p2 = Process(target=runWebapp, args=(8081,))
p1.start()
p2.start()