首页 > 解决方案 > 无法通过 group_send 向 Django 频道发送消息

问题描述

我想使用 Django 频道通过频道发送消息。这就是我正在做的。

我首先创建一个消费者。我能够回显收到的消息。但是,无法将消息发送到特定的频道/组。

class Consumer(AsyncJsonWebsocketConsumer):
    """Consumer."""

    def _get_connection_id(self):
        return ''.join(e for e in self.channel_name if e.isalnum())

    async def connect(self):
        scope = self.scope
        user_id = str(scope['user'].user_id)
        connection_id = self._get_connection_id()
        # Adding connection to DB.
        obj = UserConnection.add(connection_id=connection_id, user_id=user_id)

        # Accept the connection
        await self.accept()

        # Adding current to group.
        await self.channel_layer.group_add(
            user_id,
            connection_id,
        )

    async def disconnect(self, close_code):
        """Remove the connection and decrement connection_count in DB."""
        connection_id = self._get_connection_id()
        user_id = str(self.scope['user'].user_id)

        UserConnection.drop(connection_id=connection_id)

        # Dropping from group.
        await self.channel_layer.group_discard(
            user_id,
            connection_id,
        )

    async def receive_json(self, data, **kwargs):
        """Receive messages over socket."""
        resp = data
        # I'm able to echo back the received message after some processing.
        await self.send(json.dumps(resp, default=str))


# This does not works.
def send_to_connection(connection_id, data):
    """Send the data to the connected socket id."""
    return get_channel_layer().group_send(connection_id, data)

现在,当我尝试发送消息时,连接的套接字没有收到消息。

>>> connection_id = UserConnection.objects.get(user_id=user_id).connection_id
>>> send_to_connection(connection_id, {'a':1})
# returns <coroutine object RedisChannelLayer.group_send at 0x109576d40>

代码中有什么问题?

标签: djangosocketswebsocketdjango-channels

解决方案


对 Channel 层的工作方式存在一些误解。让我试着把它弄清楚。当客户端连接到 Channels 服务器时,会为该客户端创建一个消费者实例或通道。如果将频道添加到组中,Django Channels 会将该信息存储在频道层中。如果要向组中的所有客户端发送消息,首先通过通道层将消息发送到它们的连接/通道,然后通道将其向下游发送到连接的客户端。

因此,在您的情况下,当您调用时group_send,它不会将消息发送到客户端应用程序,因为它没有关于 websocket 连接的信息,而是发送到客户端应用程序的消费者实例。然后,该消费者实例需要获取消息并将其转发给客户端。

按照文档中的示例,这是您需要做的:

    async def receive_json(self, data, **kwargs):
        """Receive messages over socket."""
        resp = data
        # I'm able to echo back the received message after some processing.
        await self.send(json.dumps(resp, default=str))
   
   # catches group messages from channel layer and forwards downstream to client 
   async def forward_group_message(self, event):
        await self.send(json.dumps(event['data'], default=str))

# Sends message to all channels in a group cia the channel layer
def send_to_connection(connection_id, data):
    """Send the data to the connected socket id."""
    return get_channel_layer().group_send(
        connection_id, 
        {"type": "forward_group_message", "data": data}
    )

请注意您要发送到通道层的事件中的type密钥。这就是 Django Channel 知道消费者的哪个方法/处理程序将通道层事件路由到的方式。您还可以使用文档中使用的点表示法,Django Channels 仍然会找到处理程序。所以你可以使用"type": "forward.group.message"


推荐阅读