abourget / gevent-socketio

Official repository for gevent-socketio
http://readthedocs.org/docs/gevent-socketio/en/latest/
BSD 3-Clause "New" or "Revised" License
1.21k stars 331 forks source link

Server-level broadcast #67

Open philipn opened 12 years ago

philipn commented 12 years ago

Currently, an individual socket can broadcast using the BroadcastMixin. But what if you want to spin up a greenlet -- independent of a particular connection -- and have it publish to all the sockets on the server? In my case I'm streaming some data from an API and want to send it to all the sockets. Here's what I'm doing:

def broadcast_event(server, ns_name, event, *args):
    pkt = dict(type="event",
               name=event,
               args=args,
               endpoint=ns_name)

    for sessid, socket in server.sockets.iteritems():
        socket.send_packet(pkt)

def send_stuff(server):
    for thing in stream:
        broadcast_event(server, '/myapp', 'echo', thing)

...

if __name__ == '__main__':
    print 'Listening on port http://0.0.0.0:8080 and on port 10843 (flash policy server)'
    server = SocketIOServer(('0.0.0.0', 8080), Application(),
        resource="socket.io", policy_server=True,
        policy_listener=('0.0.0.0', 10843))
    gevent.spawn(send_things, server)
    server.serve_forever()

Would this broadcast pattern be valuable to include as a method on the SocketIOServer class?

philipn commented 12 years ago

It looks like we can also do this, which is a little awkward:

def broadcast_msg(server, tweet):
    for socket in server.sockets.values():
        if '/tweets' in socket:
            socket['/tweets'].emit('tweet', tweet)

but better because we don't have to manually construct the packet object (which a user of gevent-socketio probably shouldn't ever have to worry about).

Awkward because I have to check to see if the namespace is in the socket before sending because of the order things happen in my example.

dswarbrick commented 12 years ago

You really ought to use something like redis or zeromq for passing messages between sockets, because a typical production deployment will prefork multiple server instances, and you would otherwise have no visibility of sessions belonging to to another server instance.

philipn commented 12 years ago

This is true of emit(), etc as well, no? If using multiple servers the message won't be sent to everyone without using some kind of external message queue or other trickery.

But for many case the simple default single-server works great.

On Aug 1, 2012, at 3:18 PM, Daniel Swarbrickreply@reply.github.com wrote:

You really ought to use something like redis or zeromq for passing messages between sockets, because a typical production deployment will prefork multiple server instances, and you would otherwise have no visibility of sessions belonging to to another server instance.


Reply to this email directly or view it on GitHub: https://github.com/abourget/gevent-socketio/issues/67#issuecomment-7441873

dswarbrick commented 12 years ago

Something like broadcast_event_not_me() is only capable of sending the event to all sockets connected to a single parent server. So if you have multiple server instances, they will need to subscribe to some sort of message queue like redis, and listen for messages from other server instances.

philipn commented 12 years ago

@dswarbrick Yeah, the SocketIO server API is by its very nature limited to a single server. The JS SocketIO server code has the notion of different stores, and you can use RedisStore to transparently interface with a redis backend - http://www.ranu.com.ar/2011/11/redisstore-and-rooms-with-socketio.html. We should probably do something similar! (Would be a fun project!) But that's a larger issue.

philipn commented 12 years ago

@dswarbrick Tracking in #72

ultrabug commented 12 years ago

@philipn : it's funny to see we both worked on the same twitter implementation and ran across the same problem.

Maybe you'll want to see my modest contribution : https://github.com/ultrabug/geventweet

philipn commented 12 years ago

Whoa, that is funny! And we did it at like the exact same time, too.

I'm not sure about the session global approach you use. I think using the server object is more natural and more in line with what the JS implementation does. What do you think?

ultrabug commented 12 years ago

@philipn : sure I agree with you on the principle, that's why I also searched for a better solution and found this issue but I think @dswarbrick is right, we ought to use a proper message queuing layer rather than trying to hack the server. Maybe I'll take the time to implement this in geventweet.

sontek commented 12 years ago

@philipn @ultrabug I'd like to discuss this more with you guys if you are interested. I have some spare time now. I think we should support scalable datastores for broadcasting like redis and zmq out of the box.