【问题标题】:Django channels page stuck on loadingDjango 频道页面卡在加载中
【发布时间】:2020-07-21 22:33:38
【问题描述】:

我创建了一个简单的 Django Channels 消费者,它连接到 Redis 通道并从该通道接收一些数据,我想将这些数据发送到前端。

消费者能够连接到消费者并接收数据;问题是,如果我在消费者运行时尝试加载页面,页面将在加载时卡住。我确定发生这种情况是因为与 Redis 通道的连接是阻塞操作,或者可能是线程问题。我是这个概念的新手,所以我决定提出一个问题。

这是我的消费者:

class EchoConsumer(AsyncConsumer):

    async def websocket_connect(self, event):
        self.send({
                'type': 'websocket.accept'
            })

        self.receive(event)

    def receive(self, event):
        redis_url = 'redis://localhost:6379/0'
        connection = redis.StrictRedis.from_url(redis_url, decode_responses=True)
        channel = 'TEST'
        params = urllib.parse.parse_qs(self.scope.get('query_string', b'').decode('utf-8'))

        pubsub = connection.pubsub(ignore_subscribe_messages=True)
        pubsub.subscribe(channel)

        for message in pubsub.listen():

            # self.send({
            #   'type': 'websocket.send',
            #   'text': message['data'],
            # })

            print(message['data'])


    async def websocket_disconnect(self, event):
        print('DISCONNECTED!')

所以发生的情况是我可以看到正在打印到控制台的数据,但是如果我尝试离开该页面并到达我网站的其他部分,该页面将在加载时卡住。谁能帮我解决这个问题?

【问题讨论】:

    标签: python django redis django-channels


    【解决方案1】:

    您可以在这里尝试做两件事。

    订阅一次并将此信息发送给所有打开的连接

    您遇到的问题是pubsub.listen(): 将永远循环(永不停止)。因此,您的消费者将永远不会继续并能够处理更多消息。

    因为(至少在这个例子中)看起来你总是用静态值(不依赖于用户的请求)来访问 redis,你最好在你的消费者之外进行这个订阅。 (在 django 命令中 https://docs.djangoproject.com/en/3.0/howto/custom-management-commands/

    然后您可以使用该命令通过通道层将这些消息发送给您订阅的消费者。

    这会让你的消费者看起来像这样

    class EchoConsumer(AsyncJsonWebsocketConsumer):
        async def on_message(self, message):
            await self.send_json(message)
    
    

    然后在打印的管理命令中,您可以使用发送消息

    
    async_to_sync(channel_layer.group_send)(
            "echo_group",
            {"type": "on.message", "rate":Rate, "quantity": Quantity, "symbol": Symbol, "order": Order},
        )
    

    为每个打开的连接订阅一次

    只有当您希望每个 websocket 连接的订阅都不同时,您才应该这样做。 (例如,您在 url/query/headers 中使用一个值,或者仅在用户使用给定过滤器值向您的消费者发送 ws 消息时订阅)。

    这样做会使LOT 更加复杂,原因如下:

    1. Redis 无法处理与 Websocket 连接一样多的打开连接。

    2. 您需要设置一个嵌套的异步任务,该任务可以处理来自 redis 的事件,并且不会阻塞其余的使用者。

    如果您仍然需要此功能,我很乐意用解决方案更新答案(但警告它会很长)。

    【讨论】:

    • 嘿!谢谢你的回答,就像你说的那样,循环不能在消费者中,我做了一些研究,频道的创建者也这么说。事情是这样的:我不会只有一个渠道,实际上大约有 600 个渠道(每个市场一个),这就是我想从消费者那里连接的原因,因为它会直接连接到感兴趣的渠道,得到仅来自该通道的数据,其他通道无效。在这种情况下,我将有大约 10 个连接(我不希望有很多用户)
    • 使用Django管理命令最大的问题是,为了广播数据,它需要连接所有的频道,所以会是600+的连接,问题就在这里。我在想如何做到这一点:找到一种方法来获取活动组(对于活动我意味着当用户打开页面时),获取活动组(市场)的名称并连接到那些 redis 频道
    • Redds 确实支持在单个连接上订阅多个频道。您将需要使用其中一个异步 Redis 库(例如 djangos redis 层使用的库:github.com/django/channels_redis)。然后,您可以为每组数据创建一个组名,消费者可以group_add 通过向他们发送 json 消息请求订阅该组数据。
    • 我将尝试写一个简短的示例(添加到上面的答案中),说明您如何在消费者中执行此操作(使用嵌套的异步任务),但我建议在大多数情况下反对它.
    • 我知道这不是一个好方法,但在这种情况下我有什么选择?使用不涉及 Redis 的完全不同的方法?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-07
    • 2021-01-13
    • 2013-02-21
    • 1970-01-01
    • 2019-11-13
    • 1970-01-01
    相关资源
    最近更新 更多