views:

880

answers:

2

Hello everyone. I'm trying to write simple web application using Tornado and JS Prototype library. So, the client can execute long running job on server. I wish, that this job runs Asynchronously - so that others clients could view page and do some stuff there.

Here what i've got:

#!/usr/bin/env/ pytthon

import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options

import os
import string
from time import sleep
from datetime import datetime

define("port", default=8888, help="run on the given port", type=int)

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("templates/index.html", title="::Log watcher::", c_time=datetime.now())

class LongHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        self.wait_for_smth(callback=self.async_callback(self.on_finish))
        print("Exiting from async.")
        return

    def wait_for_smth(self, callback):
        t=0
        while (t < 10):
            print "Sleeping 2 second, t={0}".format(t)
            sleep(2)
            t += 1
        callback()

    def on_finish(self):
        print ("inside finish")
        self.write("Long running job complete")
        self.finish()



def main():
    tornado.options.parse_command_line()

    settings = {
        "static_path": os.path.join(os.path.dirname(__file__), "static"),
        }

    application = tornado.web.Application([
        (r"/", MainHandler),
        (r"/longPolling", LongHandler)
        ], **settings
    )
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()


if __name__ == "__main__":
    main()

This is server part. It has main view (shows little greeting, current server time and url for ajax query, that executes long running job. If you press a button, a long running job executes. And server hangs :( I can't view no pages, while this job is running. Here is template page:

<html>
<head>
    <title>{{ title }}</title>

    <script type="text/javascript" language="JavaScript" src="{{ static_url("js/prototype.js")}}"></script>


    <script type='text/javascript' language='JavaScript'>
        offset=0
        last_read=0

        function test(){
            new Ajax.Request("http://172.22.22.22:8888/longPolling",
            {
                method:"get",
                asynchronous:true,
                onSuccess: function (transport){
                    alert(transport.responseText);
                }
            })
        }


    </script>
</head>
<body>
    Current time is {{c_time}}
    <br>
    <input type="button" value="Test" onclick="test();"/>
</body>
</html>

what am I doing wrong? How can implement long pooling, using Tornado and Prototype (or jQuery)

PS: I have looked at Chat example, but it too complicated. Can't understand how it works :(

PSS Download full example

+1  A: 

Tornado is single-threaded web server. Your while loop in wait_for_smith method is blocking Tornado.

You can rewrite that method like this:

def wait_for_smth(self, callback, t=10):
    if t:
        print "Sleeping 2 second, t=%s" % t
        tornado.ioloop.IOLoop.instance().add_timeout(time.time() + 2, lambda: self.wait_for_smth(callback, t-1))
    else:
        callback()

You need to add import time at the top to make this work.

Anand Chitipothu
A: 

I've converted Tornado's chat example to run on gevent. Take a look at the live demo here and the explanation and source code here.

It uses lightweight user-level threads (greenlets) and is comparable in speed/memory use with Tornado. However, the code is straightforward, you can call sleep() and urlopen() in your handlers without blocking the whole process and you can spawn long running jobs that do the same. Under the hood the application is asynchronous, powered by an event loop written in C (libevent).

You can read the introduction here.

Denis Bilenko