7

The request handlers are as follows:

class TestHandler(tornado.web.RequestHandler):    #  localhost:8888/test
    @tornado.web.asynchronous
    def get(self):
        t = threading.Thread(target = self.newThread)
        t.start()

    def newThread(self):
        print "new thread called, sleeping"
        time.sleep(10)
        self.write("Awake after 10 seconds!")
        self.finish()

class IndexHandler(tornado.web.RequestHandler):           #   localhost:8888/ 
    def get(self):
        self.write("It is not blocked!")
        self.finish()

When I GET localhost:8888/test, the page loads 10 seconds and shows Awake after 10 seconds; while it is loading, if I open localhost:8888/index in a new browser tab, the new index page is not blocked and loaded instantly. These fit my expectation.

However, while the /test is loading, if I open another /test in a new browser tab, it is blocked. The second /test only starts processing after the first has finished.

What mistakes have I made here?

2 Answers 2

7

What you are seeing is actually a browser limitation, not an issue with your code. I added some extra logging to your TestHandler to make this clear:

class TestHandler(tornado.web.RequestHandler):    #  localhost:8888/test
    @tornado.web.asynchronous
    def get(self):
        print "Thread starting %s" % time.time()
        t = threading.Thread(target = self.newThread)
        t.start()

    def newThread(self):
        print "new thread called, sleeping %s" % time.time()
        time.sleep(10)
        self.write("Awake after 10 seconds!" % time.time())
        self.finish()

If I open two curl sessions to localhost/test simultaneously, I get this on the server side:

Thread starting 1402236952.17
new thread called, sleeping 1402236952.17
Thread starting 1402236953.21
new thread called, sleeping 1402236953.21

And this on the client side:

Awake after 10 seconds! 1402236962.18
Awake after 10 seconds! 1402236963.22

Which is exactly what you expect. However in Chromium, I get the same behavior as you. I think that Chromium (perhaps all browsers) will only allow one connection at a time to be opened to the same URL. I confirmed this by making IndexHandler run the same code as TestHandler, except with slightly different log messages. Here's the output when opening two browser windows, one to /test, and one to /index:

index Thread starting 1402237590.03
index new thread called, sleeping 1402237590.03
Thread starting 1402237592.19
new thread called, sleeping 1402237592.19

As you can see both ran concurrently without issue.

Sign up to request clarification or add additional context in comments.

2 Comments

I can't believe this is the browser's problem.... I took me a whole day trying to figure out what happened!! Thanks so much!
This was driving me crazy for an hour now.
0

I think you picked the "wrong" test for checking parallel GET requests, that's because you're using a blocking function for your test: time.sleep(), which its behavior doesn't really occur when you simply render an HTML page ...

What happens is, that the def get() ( which handle all GET requests ) is actually being blocked when you use time.sleep it cannot process any new GET requests, puts them in some kind of "queue".

So if you really want to test sleep() - use the Tornado non-blocking function: tornado.gen.sleep()

Example:

from tornado import gen

@gen.coroutine
def get(self):        
    yield self.time_wait()       

@gen.coroutine
def time_wait(self):
    yield gen.sleep(15)
    self.write("done")

Open multiple tabs in your browser, then you'll see that all requests are being processed when they arrive w/o "queueing" the new requests that comes in ..

6 Comments

While it is true that thread.sleep() is normally bad to use in a non-blocking application, because the sleep call is farmed off to a background thread in this case, it doesn't block the second get() call. This is proven by the fact that sending concurrent requests with curl works normally. The problem is with the web browser serializing the requests to the same url.
what do you mean ? the client still wait for the request ( because of the yield ) - but at least it's not blocking the server from processing other requests ... that's what I was trying to say ..
The time.sleep() call in the question doesn't block the server from processing other requests, because it's done in a background thread, not on the tornado ioloop thread.
in a single-thread Tornado server it does. I've tested it. my users were "queued" and only when the time.sleep finished, the 2nd user got processed the get request. time.sleep is not in a background thread -> but if you'll use tornado.gen.sleep - then you'd be right
In the original question, the call to time.sleep() is explicitly made in a background thread. Note the t = threading.Thread(target = self.newThread) call in the get method. Take a look at my answer - the OPs code that uses time.sleep() does not block the second call if you use curl, or make the second call from a different browser. You are right that in general using time.sleep() is not the right thing to do in a non-blocking application, but it is not the problem the OP was having in this case. He would have had the same issue with tornado.gen.sleep.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.