无法通过group_send将消息发送到Django通道

问题描述:

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

I want to send a message over a channel using Django Channels. This is how I'm doing.

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

I create a consumer first. I'm able to echo back the received messages. However, not able to send messages to a specific channel/group.

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)

现在当我尝试发送消息时,

Now when I try to send a message, the connected socket does not receives the message.

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

代码有什么问题?

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

There is a bit of a misunderstanding on how Channel layers work. Let me try to clear it up. When a client connects to the Channels server, a consumer instance or channel is created for that client. If you add the channel to a group, Django Channels stores that information in the channel layer. If you want to send a message to all clients in a group, first you send it to their connections/channels via the channel layer, then the channels will send it downstream to the connected clients.

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

So, in your case, when you call group_send, it is not sending the message to the client application as it has no information about the websocket connection, but to the client application's consumer instance. That consumer instance then needs to pick up the message and forward it to the client.

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

Following the example in the docs, this is what you need to do:

    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仍会找到处理程序。因此,您可以使用类型: forward.group.message

Note the type key in the event you're sending to the channel layer. That is how Django Channel knows which method/handler of the consumer to route the channel layer event to. You can also use the dot notation employed in the docs and Django Channels will still find the handler. So you can use "type": "forward.group.message"