【问题标题】:How to run an aiohttp server in a thread?如何在线程中运行 aiohttp 服务器?
【发布时间】:2020-01-01 20:28:44
【问题描述】:

这个线程中的 aiohttp 服务器示例失败并出现RuntimeError: There is no current event loop in thread 'Thread-1'. 错误:

import threading
from aiohttp import web


def aiohttp_server():
    def say_hello(request):
        return web.Response(text='Hello, world')

    app = web.Application(debug=True)
    app.add_routes([web.get('/', say_hello)])
    web.run_app(app)


t = threading.Thread(target=aiohttp_server)
t.start()

如何在线程中运行aiohttp服务器?

【问题讨论】:

标签: python python-asyncio aiohttp


【解决方案1】:

在主线程中创建handler,在子线程中手动创建事件循环。

import asyncio
import threading
from aiohttp import web


def aiohttp_server():
    def say_hello(request):
        return web.Response(text='Hello, world')

    app = web.Application(debug=True)
    app.add_routes([web.get('/', say_hello)])
    handler = app.make_handler()
    return handler


def run_server(handler):
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    server = loop.create_server(handler, host='127.0.0.1', port=8089)
    loop.run_until_complete(server)
    loop.run_forever()


t = threading.Thread(target=run_server, args=(aiohttp_server(),))
t.start()

更新

对于新的aiohttp,请使用以下内容,感谢@Auyer 的通知。

import asyncio
import threading
from aiohttp import web


def aiohttp_server():
    def say_hello(request):
        return web.Response(text='Hello, world')

    app = web.Application()
    app.add_routes([web.get('/', say_hello)])
    runner = web.AppRunner(app)
    return runner


def run_server(runner):
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(runner.setup())
    site = web.TCPSite(runner, 'localhost', 8080)
    loop.run_until_complete(site.start())
    loop.run_forever()


t = threading.Thread(target=run_server, args=(aiohttp_server(),))
t.start()

【讨论】:

  • 此示例代码不包含线程使用示例。如何用线程测试它?
  • 你的意思是0bin.net/paste/…?它产生RuntimeError: set_wakeup_fd only works in main thread 错误。
  • 嗯,你是对的。我们必须在主线程中创建处理程序,然后使用子线程执行loop。我会更新我的答案。
  • 是否在线程的情况下执行此操作,我不知道,但是您可以在 say_hello 之前使用 async def 使其异步。 make_handler 非常有用!这有助于解决另一个问题。
  • DeprecationWarning: Application.make_handler(...) is deprecated, use AppRunner API instead. 我正在为这个警告寻找解决方案,如果我找到它会发布给她。
【解决方案2】:

我们必须在主线程中使用app.make_handler处理程序,例如:

import asyncio
import threading
from aiohttp import web

loop = asyncio.get_event_loop()


def say_hello(request):
    return web.Response(text='Hello, world')


app = web.Application(debug=True)
app.add_routes([web.get('/', say_hello)])

handler = app.make_handler()
server = loop.create_server(handler, host='127.0.0.1', port=8080)


def aiohttp_server():
    loop.run_until_complete(server)
    loop.run_forever()


t = threading.Thread(target=aiohttp_server)
t.start()

【讨论】:

  • 这是问题的一部分吗?您不应该添加答案而不是编辑问题。
  • 你在“滥用”主线程的事件循环,这可能在简单的测试代码中工作,但总的来说这不是一个好主意。要么自己创建一个(如@Sraw 的回答),要么只从线程内部调用get_event_loop()(根据its source code,在这种情况下它将为线程创建一个新的事件循环)
  • @tevemadar get_event_loop() 只会在主线程中创建一个新的事件循环,看这个条件:isinstance(threading.current_thread(), threading._MainThread)
  • @Sraw 哎呀,我还以为是内部if
  • @Sraw 不,这是对问题的回应。
【解决方案3】:

有一个用于此用例的新 API:

https://docs.aiohttp.org/en/stable/web_advanced.html#application-runners

from aiohttp import web
import asyncio


async def healthz(request):
    return web.Response(text="OK")

app = web.Application()
app.add_routes([web.get("/", healthz)])


async def runner():
    runner = web.AppRunner(app)
    await runner.setup()
    site = web.TCPSite(runner, "localhost", 8080)
    await site.start()


loop = asyncio.get_event_loop()
loop.run_until_complete(runner())

【讨论】:

    【解决方案4】:

    可以使用web.run_app,只需创建一个新的事件循环(并设置handle_signals=False以避免RuntimeError: set_wakeup_fd only works in main thread):

    import threading
    import asyncio
    from aiohttp import web
    
    def aiohttp_server():
        def say_hello(request):
            return web.Response(text='Hello, world')
    
        asyncio.set_event_loop(asyncio.new_event_loop())  # create a new event loop here
        app = web.Application(debug=True)
        app.add_routes([web.get('/', say_hello)])
        web.run_app(app, handle_signals=False)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-12-23
      • 1970-01-01
      • 2016-11-27
      • 1970-01-01
      • 1970-01-01
      • 2021-08-12
      • 2021-06-09
      • 2013-09-02
      相关资源
      最近更新 更多