Search code examples
pythontornado

Handle multiple incoming requests with Python tornado


I have a code which works fine for one user request. However, it can not handle multiple requests, it waits for one requests to finish and then handles second requests. How can I handle multiple requests concurrently.

import tornado.ioloop
import tornado.web
import tornado.websocket
from tornado import gen
import random
import time
import sys

This is a class that simulates some incoming data.

class Message():


    def __init__(self):
        self.dct = {'foo': 0, 'bar': 0, 'durrr': 0}
        self.keys = list(self.dct.keys())


    def update_key(self):
        dct_key = random.choice(self.keys)
        ran = random.choice(range(10, 21))
        self.dct[dct_key] += ran
        if self.dct[dct_key] >= 100:
            self.dct[dct_key] = 'Loading Completed'
            self.keys.remove(dct_key)


    def is_completed(self):
        return True if self.keys == [] else False


    def __str__(self):
        strng = ''
        for key, value in self.dct.items():
            if type(value) == int:
                strng += '{}: {}% completed.<br>'.format(key, value)
            else:
                strng += '{}: {}<br>'.format(key, value)
        return strng

This class sends data through socket.

class EchoWebSocket(tornado.websocket.WebSocketHandler):


    def open(self):
        print("WebSocket opened")


    def on_message(self, message):
        msg = Message()
        while not msg.is_completed():
            msg.update_key()
            try:
                fut = self.write_message('Download progress for user xyz:<br>{}Download in progress! Please wait...<br>'.format(msg.__str__()))
                print(fut)
            except tornado.websocket.WebSocketClosedError:
                print('WebSocket is closed')
                break
            time.sleep(1)
        self.write_message('Download progress for user xyz:<br>{}Download completed. You may proceed.'.format(msg.__str__()))
        #sys.exit('Program terminated.')


    def on_close(self):
        print("WebSocket closed")

Main class is simple. Renders some html. The main engine is in EchoWebSocket.

class Main(tornado.web.RequestHandler):


    def get(self):
        self.render('counter2.html', title='Websockets')


application = tornado.web.Application([
    (r"/", Main),
    (r"/websocket", EchoWebSocket),
])


if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

And the html:

<!doctype html>
<html>
   <head>
       <title>Tornado Test</title>
       <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js"></script>
    </head>
    <body>
        <script>    
            $(document).ready(function () {
                var ws = new WebSocket("ws://localhost:8888/websocket");
                ws.onopen = function() {
                    ws.send("Hello, world");
                };
                ws.onmessage = function (evt) {
                    document.getElementById("myDIV").innerHTML = evt.data + "<br>";
                };
            });
        </script>
        <div id="myDIV"></div>
  </body>
</html>

Solution

  • You're using time.sleep(1). But time.sleep is a blocking function, that means it will stop the whole server and nothing else will be able to run during that time.

    This has also been mentioned in Tornado FAQs page.

    What you need is an asynchronous sleep function. Tornado has gen.sleep. Use it like this:

    async def my_func():
        ...
        await gen.sleep(1)