12

This example of aiohttp server in a thread fails with an RuntimeError: There is no current event loop in thread 'Thread-1'. error:

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

How to run a aiohttp server in thread?

1

4 Answers 4

7

Create handler in main thread and manually create an event loop in child thread.

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

Update

For new aiohttp, use the following, thank @Auyer for notification.

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()
Sign up to request clarification or add additional context in comments.

6 Comments

This example code don't include thread usage example. How can i test it with thread ?
You mean like that 0bin.net/paste/… ? It produce RuntimeError: set_wakeup_fd only works in main thread error.
Well, you are right. We have to create handler in main thread and then use child thread to execute loop. I will update my answer.
Whether you'd do this in the case of threading or not, I dont know, however you can use an async def before the say_hello to have it async. The make_handler is very useful! This helped solve another issue.
DeprecationWarning: Application.make_handler(...) is deprecated, use AppRunner API instead. I'm looking for a solution for this warning, and will post her if I find it.
|
3

There's a new API intended for this use case:

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

Comments

2

We must use app.make_handler handler in main thread, example:

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

5 Comments

Is this a part of question? You shouldn't add an answer instead of editing question.
You are "abusing" the event loop of the main thread, which may work in a simple test code, but it is not a good idea in general. Either create one yourself (as in @Sraw's answer) or just call get_event_loop() from inside the thread (according to its source code it will create a new event loop for the thread in this case)
@tevemadar get_event_loop() will only create a new event loop in main thread, see this condition: isinstance(threading.current_thread(), threading._MainThread)
@Sraw oops, I thought it was an inner if
@Sraw no, it is a response of question.
1

It's possible to use web.run_app, just create a new event loop (and set handle_signals=False to avoid 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)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.