WestCoastProjects
WestCoastProjects

Reputation: 63022

How to run an aiohttp web application in a secondary thread

The following code taken from the aiohttp docs https://docs.aiohttp.org/en/stable/ does work:

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

if __name__ == '__main__':
  web.run_app(app)

enter image description here

But having the webserver hijack the main thread is not acceptable: the webserver should be on a separate non-main thread and subservient to the main backend application.

I can not determine how to run the webapp on a secondary thread. Here is what I have tried:

  1. It is not possible to run the snippet of code in ipython repl:

I tried to run it this way:

#if __name__ == '__main__':
web.run_app(app)

and am notified something about no current event loop

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/IPython/core/interactiveshell.py", line 3293, in run_code
    async def run_code(self, code_obj, result=None, *, async_=False):
  File "<ipython-input-8-344f41746659>", line 13, in <module>
    web.run_app(app)
  File "/usr/local/lib/python3.8/site-packages/aiohttp/web.py", line 393, in run_app
    def run_app(app: Union[Application, Awaitable[Application]], *,
  File "/usr/local/Cellar/[email protected]/3.8.1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/events.py", line 628, in get_event_loop
    def get_event_loop(self):
RuntimeError: There is no current event loop in thread 'Thread-11'.

So then .. what it can only be run in main? I'm missing something here..

I tried running in another standalone script but on a subservient 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)

But that gives basically the same error:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/local/Cellar/[email protected]/3.8.1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self.run()
  File "/usr/local/Cellar/[email protected]/3.8.1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "/git/bluej/experiments/python/aio_thread.py", line 12, in runWebapp
    web.run_app(app)
  File "/usr/local/lib/python3.8/site-packages/aiohttp/web.py", line 409, in run_app
    loop = asyncio.get_event_loop()
  File "/usr/local/Cellar/[email protected]/3.8.1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/events.py", line 639, in get_event_loop
    raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Thread-1'.

So how do I get this webapp off the main thread and make it play along with the other threads in my application

Upvotes: 2

Views: 2459

Answers (1)

Jared Smith
Jared Smith

Reputation: 21926

Here you go:

import http.server
import threading
import socketserver

PORT = 8000

Handler = http.server.SimpleHTTPRequestHandler

def serve_forever():
    with socketserver.TCPServer(("", PORT), Handler) as httpd:
        httpd.serve_forever()

if __name__ == "__main__":
    threading.Thread(target=serve_forever).start()
    while 1:
        x = input("enter a number")
        print("You entered {}".format(x))

N.B. this is a neat party trick, but not necessarily useful for production work: the documentation for the http.server module says in flaming red letters at the top of the doc page not to use it in production. But almost all python webserver frameworks operate as WSGI servers and aren't designed to work the way you seem to want them to: they generally expect to be run by something else like gunicorn or apache.

I strongly recommend if you need an HTTP server for e.g. monitoring a running app that you use asyncio instead and use coros for everything, but you can roll your own threading scenario as above if you really want to. You can see that you can still interact with the shell in the infinite input loop while you can also curl localhost:8000 to get an HTML page containing a directory listing.

Just pass in a non-default handler of your own creation to do other stuff like return app state in JSON format.

Upvotes: 0

Related Questions