Overv
Overv

Reputation: 8529

Python WebSocket server that forwards messages from other server

My server has access to an API that receives real-time updates over a TCP socket (using ZeroMQ in broadcast mode).

I'd like to build a webservice that displays these updates to people on a website. To forward these updates, a constant connection with all clients through WebSockets seems like the best solution.

I have a good idea on how to do these things separately, but how do I efficiently combine these two in one Python server application?

My idea currently in pseudo code is something like this:

while True:
    acceptNewWebSocketConnections()
    update = receiveUpdateZeroMQ()
    sendMessageToAllWebSockets(update)

What kind of Python libraries would support this kind of model and how do I implement it while preventing congestion?

Upvotes: 1

Views: 1701

Answers (2)

KimiNewt
KimiNewt

Reputation: 529

As an alternative to what mguijarr said, you can use Tornado with zmq together in an IOLoop.

An IOLoop is something that basically checks if sockets are available to read from, and when they are calls a given callback. It runs on a single thread but since it wastes almost no time on just "waiting", it is often faster!

Using tornado with ZMQ is quite simple, so an architecture like you're describing can be like this:

from zmq.eventloop import zmqstream, ioloop
import tornado.web
from tornado import websocket

class MyBroadcastWebsocket(websocket.WebSocketHandler):
    clients = set()

    def open(self):
        self.clients.add(self)

    @classmethod
    def broadcast_zmq_message(cls, msg):
        for client in cls.clients:
            client.write_message('Message:' + str(msg)) # Send the message to all connected clients

    def on_close(self):
        self.clients.remove(self)

def run():
    ioloop.install()
    my_stream = zmqstream.ZMQStream(my_zmq_socket) # i.e. a pull socket
    my_stream.on_recv(MyBroadcastWebsocket.broadcast_zmq_message) # call this callback whenever there's a message

if __name__ == "__main__":
    application = tornado.web.Application([
        (r"/websocket", MyBroadcastWebsocket),
    ])
    application.listen(8888)
    ioloop.IOLoop.instance()

That's literally it! You can look at more info in zmq's or tornado's docs.

Upvotes: 2

mguijarr
mguijarr

Reputation: 7910

I would recommend to use gevent to have a single event loop for all connections, since the Python binding of zmq supports gevent (import zmq.green as zmq) and you also have a gevent websockets implementation available here: gevent-websocket; gevent is compatible with many web servers thanks to WSGI.

Upvotes: 0

Related Questions