【问题标题】:Running any web server event loop on a secondary thread在辅助线程上运行任何 Web 服务器事件循环
【发布时间】:2020-04-02 13:14:00
【问题描述】:

我们有一个处理消息传递/队列、数据库查询和计算机视觉的丰富后端应用程序。我们需要的另一个特性是 tcp 通信——最好是通过 http。关键是:这主要不是一个网络应用程序。我们希望为不同的目的设置一组 http 通道。是的 - 我们了解包括主题和发布-订阅在内的消息传递:但基于直接 tcp 的请求/响应也有它的位置。

我已经查看并试用了六个 python http web 服务器。它们隐含或显式描述了在 main 线程上运行 event loop 的要求。这对我们来说是本末倒置:main 线程已经被其他任务占用,包括其他活动的协调。

为了说明预期的结构,我将从我的aiohttp 特定问题How to run an aiohttp web application in a secondary thread 中提取代码。在那个问题中,我尝试在另一个独立脚本中运行,但在从属线程上运行:

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'.

这个错误的意思是“嘿,你没有在 main 线程上运行这个”。

我们可以在这里逻辑地将aiohttp 替换为其他网络服务器。这种要求 Web 服务器的事件处理循环在辅助线程上运行的方法是否适用?到目前为止,我还尝试过cherrypytornadoflask

请注意,我没有尝试过的一个著名的网络服务器是django。但这似乎需要围绕django 的预期目录结构(/必需?)对应用程序进行广泛的重组。我们不希望这样做,因为该应用程序有一系列其他用途,可以取代拥有 http 服务器的这种杂耍。

看过的一种方法是asyncio。我不明白它是否可以支持在侧线程上运行事件循环:如果可以,那么它将是这个问题的答案。

在任何情况下,是否有任何 Web 服务器明确支持让其事件循环脱离主线程?

【问题讨论】:

标签: python multithreading embeddedwebserver


【解决方案1】:

您可以在辅助线程上创建和设置事件循环:

asyncio.set_event_loop(asyncio.new_event_loop())

cherrypyflask 没有这个已经可以工作了; tornado 适用于此。

aiohttp 上,调用loop.add_signal_handler() 时会出现另一个错误:

ValueError: set_wakeup_fd 只在主线程中起作用

您需要跳过它,因为only the main thread of the main interpreter is allowed to set a new signal handler,这意味着在辅助线程上运行的 Web 服务器无法直接处理信号以进行优雅退出。

示例:aiohttp

  1. 在调用run_app()之前设置事件循环。
    aiohttp 3.8+ 已经uses a new event loop in run_app(),所以你可以跳过这个。

  2. 在调用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()

【讨论】:

  • so by web.run_app(app, handle_signals=False) 这有什么影响/需要主线程的一些额外参与吗?
  • 不,只是aiohttp 不能优雅退出。
  • 啊好的。我认为这在 22 个月前可能真的对我有所帮助
【解决方案2】:

任何 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()

【讨论】:

  • 这是一个有用的结构。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-30
相关资源
最近更新 更多