-->

Tornado long polling requests

2019-07-22 11:06发布

问题:

Below is the most simple example of my issue:

When a request is made it will print Request via GET <__main__.MainHandler object at 0x104041e10> and then the request will remain open. Good! However, when you make another request it does not call the MainHandler.get method until the first connection has finished.

How can I get multiple requests into the get method while having them remain long-polling. I'm passing arguments with each request that will get different results from a pub/sub via redis. Issue is that I only get one connection in at a time. Whats wrong? And why is this blocking other requests?

import tornado.ioloop
import tornado.web
import os


class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        print 'Request via GET', self


if __name__ == '__main__':
    application = tornado.web.Application([
        (r"/", MainHandler)])

    try:
        application.listen(int(os.environ.get('PORT', 5000)))
        tornado.ioloop.IOLoop.instance().start()
    except KeyboardInterrupt:
        tornado.ioloop.IOLoop.instance().stop()

Diagram Left: As described in issue above. The requests are not handled in the fashion requested in right diagram. Diagram on the right I need the requests (a-d) to be handled by the RequestHandler and then wait for the pub/sub to announce their data.

       a  b   c   d
       +  +   +   +                        ++          a    b   c   d
       |  |   |   |                        ||          +    +   +   +
       |  |   |   |                        ||          |    |   |   |
       |  |   |   |                        ||          |    |   |   |
       |  |   |   |                        ||          |    |   |   |
       |  v   v   v                        ||          |    |   |   |
   +---|-----------------------------+     ||    +-----|----|---|---|------------------+
   |   |                             |     ||    |     |    |   |   |                  |
   |   +               RequestHandler|     ||    |     +    +   +   +     RequestHan.  |
   |   |                             |     ||    |     |    |   |   |                  |
   +---|-----------------------------+     ||    +-----|----|---|---|------------------+
   +---|-----------------------------+     ||    +-----|----|---|---|------------------+
   |   |                             |     ||    |     |    |   |   |                  |
   |   +                Sub/Pub Que  |     ||    |     v    +   v   v         Que      |
   |   |                             |     ||    |          |                          |
   +---|-----------------------------+     ||    +----------|--------------------------+
   +---|-----------------------------+     ||    +----------|--------------------------+
       |                                   ||               |
       |                 Finished          ||               |               Finished
       v                                   ||               v
                                           ||
                                           ||
                                           ||
                                           ||
                                           ||
                                           ||
                                           ||
                                           ++

If this is accomplishable with another programming language please let me know.

Thank you for your help!

回答1:

From http://www.tornadoweb.org/en/stable/web.html#tornado.web.asynchronous:

tornado.web.asynchronous(method)

...

If this decorator is given, the response is not finished when the method returns. It is up to the request handler to call self.finish() to finish the HTTP request. Without this decorator, the request is automatically finished when the get() or post() method returns.

You have to finish get method explicitly:

import tornado.ioloop
import tornado.web
import tornado.options

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)

class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        print 'Request via GET', self
        self.finish()


if __name__ == '__main__':
    application = tornado.web.Application([
        (r"/", MainHandler)])

    try:
        application.listen(options.port)
        tornado.ioloop.IOLoop.instance().start()
    except KeyboardInterrupt:
        tornado.ioloop.IOLoop.instance().stop()