需要帮助理解Python中的Comet(与Django一起使用)

28 投票
3 回答
6963 浏览
提问于 2025-04-16 15:27

我花了整整两天的时间,还是搞不懂Python中Comet的各种选择和配置。我看了这里所有的回答,还找了很多博客文章。现在我感觉快要崩溃了,所以如果这个问题有什么不对的地方,我深表歉意。

我对这一切都是全新的,之前做的只是一些简单的非实时网站,后端用的是PHP或Django,服务器是Apache。

我的目标是创建一个实时聊天应用,希望能和Django结合,用于用户管理、身份验证、模板等。

每次我读到一个工具时,它总是说我还需要在它之上再加一个工具,感觉这是一条永无止境的链子。

首先,有人能把完成这个工作的所有工具分类吗?
我读到了不同的服务器、网络库、引擎、客户端的JavaScript,还有其他的东西,我完全不知道该从哪里入手。我从没想过会这么复杂。

Twisted / Twisted Web似乎很受欢迎,但我不知道怎么把它整合进来,或者还需要什么(我猜至少需要客户端的JS)。

如果我理解得没错的话,Orbited是基于Twisted构建的,那我还需要其他的东西吗?

Gevent和Eventlet跟Twisted是同一类工具吗?我还需要搭配它们使用什么其他的东西吗?

像Celery、RabbitMQ或者像Redis这样的键值存储在这个过程中扮演什么角色?我对消息队列的概念不是很理解。它们是必需的吗?提供什么服务?

有没有完整的聊天应用教程可以参考一下?

如果有人能帮我突破这个心理障碍,我会非常感激。如果我遗漏了什么,请随时问我。我知道这个问题比较复杂。

3 个回答

3

Redis 是一个持久化存储层,它还支持原生的 发布/订阅 功能。这意味着你不需要一直去数据库里查询新消息,而是可以订阅一个频道,消息会自动推送给你。

我找到一个 可以运行的例子,展示了你描述的系统。关键的部分在于 socketio 视图

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()

我们一步一步来看这个视图:

  1. 设置 socket.io,获取一个 Redis 客户端和当前用户。
  2. 使用 Gevent 注册一个“订阅者”——这个订阅者会接收来自 Redis 的消息,并把它们转发给客户端浏览器。
  3. 运行一个“发布者”,它会从 socket.io(用户的浏览器)接收消息,并把这些消息推送到 Redis。
  4. 重复以上步骤,直到 socket 断开连接。

Redis Cookbook 提供了关于 Redis 的 更多细节,还讨论了如何持久化消息。

关于你问题的其他部分:Twisted 是一个基于事件的网络库,可以看作是这个应用中 Gevent 的替代品。它功能强大,但根据我的经验,调试起来比较困难。

Celery 是一个“分布式任务队列”——简单来说,它可以让你把工作分散到多台机器上。这里的“分布式”意味着机器之间需要某种传输方式。Celery 支持多种传输方式,包括 RabbitMQ(还有 Redis)。

在你的例子中,只有在你需要对每条消息进行一些耗时处理,比如扫描脏话之类的情况下,Celery 才合适。即便如此,还是需要有代码来监听 socket.io 的回调,以便启动 Celery 任务。

(如果你还没完全糊涂,Celery 本身可以使用 Gevent 作为其底层的并发库。)

希望这些信息对你有帮助!

5

我理解你的感受,这几个月我也经历了类似的研究。虽然我还没时间去处理正式的文档,但我有一个使用Django和socket.io以及tornadio的工作示例,地址在http://bitbucket.org/virtualcommons/vcweb。我原本希望能通过队列实现Django服务器和tornadio服务器之间的直接通信(也就是说,Django视图中的逻辑将消息推送到队列,然后由tornadio处理,再将这个消息的json编码版本推送给所有感兴趣的订阅者),但这部分我还没完全实现。目前我设置的方式包括:

  1. 一个外部的tornado(tornadio)服务器,它在另一个端口上运行,接受socket.io请求,并与Django模型一起工作。这个服务器进程对数据库的唯一写入是需要存储的聊天消息。它可以完全访问所有Django模型等,所有实时交互都需要直接通过这个服务器进程进行。
  2. 需要实时访问的Django模板页面包含socket.io的JavaScript,并与tornadio服务器建立直接连接。

我也研究过orbitedhookboxgevent,但最终选择了socket.io + tornado,因为这让我觉得可以写出最干净的JavaScript + Python代码。不过,我可能错了,因为我只是去年开始学习Python/Django的。

8

你可以使用 Socket.IO。它有适配器可以和 gevent 和 tornado 一起使用。你可以看看我写的关于在 Django 中使用 gevent-socketio 的博客文章,链接在这里: http://codysoyland.com/2011/feb/6/evented-django-part-one-socketio-and-gevent/

撰写回答