Broadcasting a message using Tornado

2019-04-13 09:31发布

问题:

I have an app sending messages which should be broadcasted to every currently connected client. Lets say the clients count could go in thousands. How do I achieve this broadcasting logic without blocking?

Sample code that I think will block:

clients = []

class Broadcaster(tornado.websocket.WebSocketHandler):
   def on_message(self, message):
       for client in clients:
           self.write_message(message)

Every example I found on the web was like the above code. There were some examples using @gen.coroutine but I don't understand how this decorator could help in this scenario.

回答1:

WebSocketHandler.write_message does not block on network I/O, so it will be pretty fast, although it can still add up if you have huge numbers of clients. I suggest doing some performance testing to see how long it will actually take to broadcast a message (remember the size of the message matters, too, and it will be much faster to write a byte string than a dict since the dict will be re-encoded as json every time). If it turns out to take longer than you can tolerate blocking in your application, add a yield gen.moment to the loop periodically:

@gen.coroutine
def broadcast_message(self, message):
    count = 0
    for client in self.clients:
        client.write_message(message)
        count += 1
        if count % 100 == 0:
            yield gen.moment


回答2:

You could avoid this for loop there if this message can go to thousands as you said. I suggest you'd use a task service, like celery, to deliver the message. Then it'd be like:

clients = []

class Broadcaster(tornado.websocket.WebSocketHandler):
   def on_message(self, message):
       for client in clients:
           celery_task(message)

and the celery_task() method will be a celery task method

@app.task
def celery_task(message):
    //logic

Then a task will be added to the queue and each task will send the message you desire to your clients. I hope that helps.