Search code examples
pythontimeouttornado

Tornado timeout block all services


I create a simple test app to check timeout in tornado import tornado.ioloop import tornado.web

class LoopHandler(tornado.web.RequestHandler):
    def get(self):
        while (True):
            print ("in loop")
        self.write("Loop, Handler")

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
        (r"/loop", LoopHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

Then I call http://localhost:8888/loop the endpoint never response because the infinite loop the problems is that http://localhost:8888/ not responding either. the question is why this happened and how can solve this?

EDIT

Update code that solve the problemn

import tornado.ioloop
import tornado.web


@unblock
class LoopHandler(tornado.web.RequestHandler):
    def get(self):
        while (True):
            print ("in loop")
        return "Loop, Handler"

@unblock        
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        return "Hello, world"

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
        (r"/loop", LoopHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

#unblock.py   
import tornado.web
import tornado.ioloop

from concurrent.futures import ThreadPoolExecutor
from functools import partial, wraps

EXECUTOR = ThreadPoolExecutor(max_workers=4)


def unblock(f):

    @tornado.web.asynchronous
    @wraps(f)
    def wrapper(*args, **kwargs):
        self = args[0]

        def callback(future):
            self.write(future.result())
            self.finish()

        EXECUTOR.submit(
            partial(f, *args, **kwargs)
        ).add_done_callback(
            lambda future: tornado.ioloop.IOLoop.instance().add_callback(
                partial(callback, future)))

    return wrapper

Solution

  • These are basics of async programming. To point you in the right direction take a look at the reactor pattern and especially at the event loop.

    The reactor pattern is one implementation technique of event-driven architecture. In simple terms, it uses a single threaded event loop blocking on resource-emitting events and dispatches them to corresponding handlers and callbacks.

    Both functions LoopHandler and MainHandler are processed in the same event loop, therefore MainHandler gets queued but never executed since the event loop is busy executing LoopHandler.

    One of the challenges (at least for me) in async programming is to be careful about blocking calls like database operations with for example SQLAlchemy, file operations, expensive calculations, etc. There are some interesting approaches using thread pools to solve this but you won't need them to get you started.

    Ah, and in case you stumbling over the first sentence of the wiki article I have linked take a look here to understand the difference between parallel and concurrent. It helped me a lot.