Will
Will

Reputation: 453

Can a Tornado WebSocketHandler receive messages asynchronously?

On the python side, I've created a WebSocketHandler.

from tornado import gen
from tornado.escape import json_decode
from tornado.websocket import WebSocketHandler

class Echo(WebSocketHandler):
    ...
    @gen.coroutine
    def on_message(self, message):
        message = json_decode(message)
        response = yield self.do_echo(message)
        self.write_message(response)

    @gen.coroutine
    def do_echo(self, message):
        # emulate long, blocking call
        sleep(randint(0, 5))
        raise gen.Return(message)

On the javascript side, I start multiple clients (different browsers) at the same time with:

var ws = new WebSocket('ws://localhost:5000/echo');

ws.onmessage = function (evt) {
    console.log(JSON.parse(evt.data));
}

for (var i = 0; i < 10; i++) {
    var msg = {
        messageid: i,
        payload: 'An echo message.'
    };
    ws.send(JSON.stringify(msg));
}

All clients, as expected, finish at roughly the same time. However, each client's received messages are logged in the exact order (messageid) that they were sent as if the WebSocketHandler is queuing messages. Python logs on the server side reflect this as well.

So then, my questions are:

Note that this is not the real code but a reasonable facsimile.

Upvotes: 2

Views: 155

Answers (1)

Ben Darnell
Ben Darnell

Reputation: 22154

You must never run "long, blocking calls" on the IOLoop thread, because that blocks everything else. That sleep call (or whatever it's standing in for), must either be rewritten to be asynchronous or handed off to another thread (which is fairly easy with concurrent.futures.ThreadPoolExecutor). See http://www.tornadoweb.org/en/stable/faq.html#why-isn-t-this-example-with-time-sleep-running-in-parallel for more on this.

Upvotes: 1

Related Questions