Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need help understanding Comet in Python (with Django)

After spending two entire days on this I'm still finding it impossible to understand all the choices and configurations for Comet in Python. I've read all the answers here as well as every blog post I could find. It feels like I'm about to hemorrhage at this point, so my utmost apologies for anything wrong with this question.

I'm entirely new to all of this, all I've done before were simple non-real-time sites with a PHP/Django backend on Apache.

My goal is to create a real-time chat application; hopefully tied to Django for users, auth, templates, etc.

Every time I read about a tool it says I need another tool on top of it, it feels like a never-ending chain.

First of all, can anybody categorize all the tools needed for this job?
I've read about different servers, networking libraries, engines, JavaScripts for the client side, and I don't know what else. I never imagined it would be this complex.

Twisted / Twisted Web seems to be popular, but I have no idea to to integrate it or what else I need (guessing I need client-side JS at least).

If I understand correctly, Orbited is built on Twisted, do I need anything else with it?

Are Gevent and Eventlet in the same category as Twisted? How much else do I need with them?

Where do things like Celery, RabbitMQ, or KV stores like Redis come into this? I don't really understand the concept of a message queue. Are they essential and what service do they provide?

Are there any complete chat app tutorials I should look at?

I'll be entirely indebted to anybody who helps me past this mental roadblock, and if I left anything out please don't hesitate to ask. I know it's a pretty loaded question.

like image 219
XOR Avatar asked Apr 10 '11 19:04

XOR


3 Answers

You could use Socket.IO. There are gevent and tornado handlers for it. See my blog post on gevent-socketio with Django here: http://codysoyland.com/2011/feb/6/evented-django-part-one-socketio-and-gevent/

like image 104
codysoyland Avatar answered Nov 10 '22 09:11

codysoyland


I feel your pain, having had to go through the same research over the past few months. I haven't had time to deal with proper documentation yet but I have a working example of using Django with socket.io and tornadio at http://bitbucket.org/virtualcommons/vcweb - I was hoping to set up direct communication from the Django server-side to the tornadio server process using queues (i.e., logic in a django view pushes a message onto a queue that then gets handled by tornadio which pushes a json encoded version of that message out to all interested subscribers) but haven't implemented that part fully yet. The way I've currently gotten it set up involves:

  1. An external tornado (tornadio) server, running on another port, accepting socket.io requests and working with Django models. The only writes this server process makes to the database are the chat messages that need to be stored. It has full access to all Django models, etc., and all real-time interactions need to go directly through this server process.
  2. Django template pages that require real-time access include the socket.io javascript and establish direct connections to the tornadio server

I looked into orbited, hookbox, and gevent but decided to go with socket.io + tornado as it seemed to allow me the cleanest javascript + python code. I could be wrong about that though, having just started to learn Python/Django over the past year.

like image 32
A Lee Avatar answered Nov 10 '22 10:11

A Lee


Redis is relevant as a persistence layer that also supports native publish/subscribe. So instead of a situation where you are polling the db looking for new messages, you can subscribe to a channel, and have messages pushed out to you.

I found a working example of the type of system you describe. The magic happens in the socketio view:

def socketio(request):
    """The socket.io view."""
    io = request.environ['socketio']
    redis_sub = redis_client().pubsub()
    user = username(request.user)

    # Subscribe to incoming pubsub messages from redis.
    def subscriber(io):
        redis_sub.subscribe(room_channel())
        redis_client().publish(room_channel(), user + ' connected.')
        while io.connected():
            for message in redis_sub.listen():
                if message['type'] == 'message':
                    io.send(message['data'])
    greenlet = Greenlet.spawn(subscriber, io)

    # Listen to incoming messages from client.
    while io.connected():
        message = io.recv()
        if message:
            redis_client().publish(room_channel(), user + ': ' + message[0])

    # Disconnected. Publish disconnect message and kill subscriber greenlet.
    redis_client().publish(room_channel(), user + ' disconnected')
    greenlet.throw(Greenlet.GreenletExit)

    return HttpResponse()

Take the view step-by-step:

  1. Set up socket.io, get a redis client and the current user
  2. Use Gevent to register a "subscriber" - this takes incoming messages from Redis and forwards them on to the client browser.
  3. Run a "publisher" which takes messages from socket.io (from the user's browser) and pushes them into Redis
  4. Repeat until the socket disconnects

The Redis Cookbook gives a little more detail on the Redis side, as well as discussing how you can persist messages.

Regarding the rest of your question: Twisted is an event-based networking library, it could be considered an alternative to Gevent in this application. It's powerful and difficult to debug in my experience.

Celery is a "distributed task queue" - basically, it lets you spread units of work out across multiple machines. The "distributed" angle means some sort of transport is required between the machines. Celery supports several types of transport, including RabbitMQ (and Redis too).

In the context of your example, Celery would only be appropriate if you had to do some sort of costly processing on each message like scanning for profanity or something. Even still, something would have to initiate the Celery task, so there would need to be some code listening for the socket.io callback.

(Just in case you weren't totally confused, Celery itself can be made to use Gevent as its underlying concurrency library.)

Hope that helps!

like image 3
bcattle Avatar answered Nov 10 '22 10:11

bcattle