使用 Django Channels 从外部 Consumer 类发送消息

Ahm*_*mad 15 python django websocket django-channels

我正在构建一个在线游戏,它使用 Django 通道 2.1.5 进行 websockets。

我能够在客户端和服务器之间建立连接,并且也能够仅在消费者类内部在它们之间发送数据:

from channels.generic.websocket import WebsocketConsumer
import json
from . import controller

class GameConsumer(WebsocketConsumer):
    def connect(self):
        self.accept()
        print("Wohooo .. Connected to client!")
        self.render()
        controller.startTurn()

    def render(self, type="render", message=None):
        self.send(controller.renderMap(type, message))

    def disconnect(self, close_code):
        print("WebSocket connection is lost...")

    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        controller.handleRecieved(text_data)
...
Run Code Online (Sandbox Code Playgroud)

现在,我想做的是从另一个模块调用函数render,它位于消费者类中

我试过这个:

from .. import consumer

def sendDeployments(owner, armies):
    type = "renderDeployments"
    message = owner + " has " + str(armies) + " to deploy"
    dummyConsumer = consumer.GameConsumer()
    consumer.GameConsumer.render(type, message)
Run Code Online (Sandbox Code Playgroud)

但是失败了,因为我不能在课外使用“self”参数。

有人能想出一种方法来实现我的目标吗?

Ps:我不在乎这个场合的同步。

Mat*_*ard 16

首先,您需要您的消费者实例来订阅一个组。

from asgiref.sync import async_to_sync

class GameConsumer(WebsocketConsumer):
    def connect(self):
        self.accept()
        self.render()
        async_to_sync(self.add_group)('render_updates_group') 
        controller.startTurn()
...
Run Code Online (Sandbox Code Playgroud)

然后,如果您在消费者之外,则需要向该组发送一条消息,以便所有注册到该组的消费者都能收到该消息。

from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync

def sendDeployments(owner, armies):
    type = "renderDeployments"
    message = owner + " has " + str(armies) + " to deploy"
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        'render_updates_group',
        {'type': 'render', 'message': message}
    )
Run Code Online (Sandbox Code Playgroud)

但是,您还需要记住在断开连接时将您的使用者从组中删除。

class GameConsumer(WebsocketConsumer):
    ....

    def disconnect(self, close_code):
         async_to_sync(self.group_name)('render_updates_group')
Run Code Online (Sandbox Code Playgroud)

如果您想限制哪些打开的连接收到此渲染消息,您需要相应地构建您的组名。

例如,如果您正在考虑某个在线游戏,那么您可能会MATCH-ID在组名称中包含一个由同一比赛的所有连接共享的名称。

对此的一个很好的参考是通道层文档,但请记住,在生产中运行它时,您需要设置一个消息传递层(通常是 Redis)。


小智 9

首先为你的消费者做一些改变,比如

from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
import json

class EventConsumer(WebsocketConsumer):
    def connect(self):
        # self.room_name = self.scope['url_route']['kwargs']['room_name']
        # self.room_group_name = 'chat_%s' % self.room_name
        self.room_name = 'event'
        self.room_group_name = self.room_name+"_sharif"
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name,
            self.channel_name
        )
        print(self.room_group_name)
        self.accept()
        print("#######CONNECTED############")

    def disconnect(self, code):
        async_to_sync(self.channel_layer.group_discard)(
            self.room_group_name,
            self.channel_name
        )
        print("DISCONNECED CODE: ",code)

    def receive(self, text_data=None, bytes_data=None):
        print(" MESSAGE RECEIVED")
        data = json.loads(text_data)
        message = data['message']
        async_to_sync(self.channel_layer.group_send)(
            self.room_group_name,{
                "type": 'send_message_to_frontend',
                "message": message
            }
        )
    def send_message_to_frontend(self,event):
        print("EVENT TRIGERED")
        # Receive message from room group
        message = event['message']
        # Send message to WebSocket
        self.send(text_data=json.dumps({
            'message': message
        }))
Run Code Online (Sandbox Code Playgroud)

然后从您的应用程序外部/任何地方调用该函数,例如

def event_triger():
    channel_layer = get_channel_layer()
    async_to_sync(channel_layer.group_send)(
        'event_sharif',
        {
            'type': 'send_message_to_frontend',
            'message': "event_trigered_from_views"
        }
    ) 
# here 'event_sharif' is your room_group_name as i defined before in consumer
# 'type' is like a command, for which method you wants to trigger in your consumer
Run Code Online (Sandbox Code Playgroud)