如何使用 Django Channels 3.0.0 向所有消费者发送消息?

Posted

技术标签:

【中文标题】如何使用 Django Channels 3.0.0 向所有消费者发送消息?【英文标题】:How to send a message to all consumers using Django Channels 3.0.0? 【发布时间】:2020-11-04 23:22:10 【问题描述】:

我正在尝试开发一个界面,其中包含一个显示所有活动用户的部分。因此,当用户连接到 WebSocket 时,我想向所有连接的消费者发送数据。

目前,我编写了一个代码,当用户连接时,它只向连接的用户发送数据。我想以某种方式将消息发送给所有活跃的用户/消费者。

这是我的 WebSocket 处理程序/视图的路径

path('test_ws/', websocket.TestConsumer.as_asgi()),

这里是处理程序

class NewsMentionConsumer(AsyncWebsocketConsumer):

groups = ["newsmention1"]
channel_name = 'news_mention'
active_users = []

async def connect(self):
    await self.channel_layer.group_add(self.groups[0], self.channel_name)
    await self.channel_layer.group_send(self.groups[0], 
        'type': 'send_updates',
        'text': json.dumps(
            'id': self.scope['user'].id,
            'username': self.scope['user'].username,
            'active_users': self.active_users
        )
    )
    await self.accept()
    self.active_users.append(self.scope['user'])

async def send_updates(self, event):
    # TODO: Make this send to all users/consumers
    await self.send(event["text"])

我在理解 django.channels 文档中的示例时遇到了问题,并尝试使用 send_group 函数,但它并没有真正起作用。有什么建议吗?

编辑:将所有用户添加到具有唯一名称的同一组中。

async def connect(self):
    await self.channel_layer.group_add(self.groups[0], self.unique_name());
    await self.channel_layer.group_send(self.groups[0],  ... );

group_send 仍然只将消息发送给请求的用户。

编辑2:添加init函数后。

现在,我有一个 channel_name,但是当我尝试将数据发送给组中的所有消费者时出现新错误:

class NewsMentionConsumer(AsyncWebsocketConsumer):

group = "newsmention1"
active_users = []

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)

async def connect(self):
    await self.channel_layer.group_add(
        self.group,
        self.channel_name,
    )

    await self.channel_layer.group_send(self.group, 
        'type': 'send_updates',
        'id': self.scope['user'].id,
        'username': self.scope['user'].username,
        'some_data_to_all_clients': some_data_to_all_clients()
    )

async def disconnect(self, close_code):
    await self.channel_layer.group_discard(self.group, self.channel_name)

async def send_updates(self, event):
    await self.channel_layer.send_group(self.group, event)


'RedisChannelLayer' object has no attribute 'send_group'

有什么建议吗?

编辑 3:

    async def connect(self):
        await self.accept()
        await self.channel_layer.group_add(
            self.group,
            self.channel_name,
        )
        self.active_users.append(
            'id': self.scope['user'].id,
            'username': self.scope['user'].username
        )
        await self.channel_layer.group_send(self.group, 
            'type': 'send_updates',
            'id': self.scope['user'].id,
            'username': self.scope['user'].username,
            'some_data_to_all_clients': some_data_to_all_clients()
        )

    async def send_updates(self, event):
        await self.send(json.dumps(event))

【问题讨论】:

多余的__init__方法有什么原因吗?除非需要,否则不应覆盖它。您仍然收到频道名称的空错误吗?您发布的错误是不同的,并且非常不言自明。您应该在send_updates 方法中调用channel_layer.send(...) 而不是send_group,因为没有这样的方法。请参考文档中的教程 channel_layer.send(...) 是否应该将数据发送给同一组中的所有消费者? 不,它没有,self.channel_layer.group_send 有,而且你已经在使用它了。 group_send 将消息广播到组中的每个通道,每个通道都包含 send_updates 方法,该方法现在通过 websocket 连接将消息发送到下游消费者。这就是为什么您需要send 方法将其发送给客户端 我将更新我的代码以显示我如何在连接函数中使用.group_send 和在send_updates 函数中使用.send(正如您所说),它仍然将消息发送到请求的仅限消费者。 我的错,它不应该是channel_layer.send,而是self.send,因为你没有通过通道层发送。如果您按照文档中的聊天教程进行操作,这将非常有帮助,因为他们已经在channels.readthedocs.io/en/stable/tutorial/part_3.html 中正确执行了这些操作 【参考方案1】:

This is a bug 的 Django 频道 3.0.0。它已在3.0.1 中修复。

【讨论】:

正确,我在一周内修复错误的两个版本之间的一天内开始使用 Django 频道。【参考方案2】:

您不应设置静态频道名称,因为它用于标识频道,因此必须是唯一的。通常的做法是使用 Django Channels 生成的频道名称。 关于向所有连接的用户发送事件,您只需要有一个组,在他们连接时将所有通道添加到该组,然后在那里发送消息。除了频道名称部分外,您所做的看起来已经很相似了。

【讨论】:

我再次尝试将所有用户添加到相同的组名和唯一的频道名称中,但 send_group 函数仍然只将消息发送给请求的用户。 您使用的是哪个通道层?您确定其他用户已连接吗?顺便说一句,不要创建自己的频道名称,使用 Django 频道分配的名称。如果您想识别用户,请为他创建一个单独的组。无论如何,这种方法更好,因为您将能够识别来自同一用户的多个连接 目前,我确信我为每个渠道/消费者使用了唯一的名称,并且它们都在同一个组中。当我尝试 self.channel_name 时,“使用由 Django 频道分配的频道”是什么意思,它总是未定义。我还是卡住了。 那么你的代码有问题。 self.channel_name 由 Django Channels 填充,通常保持原样。如果出现错误,请使用它并发布您的代码和堆栈跟踪。另外,您使用的是哪个通道层?可以显示 CHANNEL_LAYER 设置吗? 抱歉,我忘记了消费者类中的 init,现在我为每个消费者设置了一个频道名称。

以上是关于如何使用 Django Channels 3.0.0 向所有消费者发送消息?的主要内容,如果未能解决你的问题,请参考以下文章

Django Channels 给我一个奇怪的错误

如何在 Heroku 上使用 Channels 和 Celery 部署 Django?

Django Channels:如何刷新发送缓冲区

如何通过 Django Channels 实现视频通话?

使用Django Channels 2上传文件

使用 Django Channels 2 上传文件