我如何订阅消费者并通知他 Django 频道的任何变化

Posted

技术标签:

【中文标题】我如何订阅消费者并通知他 Django 频道的任何变化【英文标题】:How can i subscribe a consumer and notify him of any changes in django channels 【发布时间】:2019-05-13 09:25:31 【问题描述】:

我目前正在构建一个允许用户一起协作和创造事物的应用程序,因为我需要一种不和谐的方式,比如群聊提要。我需要能够为已登录的用户订阅项目以获取通知。 我有一个方法open_project,它从用户选择的项目中检索详细信息,我用它来为他订阅该项目的任何更新。

所以我可以想到 2 种方法来做到这一点。我在连接函数中创建了一个实例变量,如下所示:

def connect(self):
    print("connected to projectconsumer...")
    self.accept()
    self.projectSessions = 

这里是open_project 方法:

def open_project(self, message):

    p = Project.objects.values("projectname").get(id=message)
    if len(self.projectSessions) == 0:
        self.projectSessions[message] = []
        pass
    self.projectSessions[message] = self.projectSessions[message].append(self)
    print(self.projectSessions[message])
    self.userjoinedMessage(self.projectSessions[message])
    message = 
    message["command"] = "STC-openproject"
    message["message"] = p
    self.send_message(json.dumps(message))

然后当用户打开一个项目时,他会被添加到projectSessions 列表中,但这不起作用(我认为)每当新用户连接到 websocket 时,他都会获得自己的项目消费者。

我想到的第二种方法是创建一个只有 1 个实例的管理类,并跟踪连接到项目的所有用户。我还没有尝试过这个,因为如果我什至在正确的球场上挥杆,我想得到一些反馈。感谢您提供任何和所有反馈。

编辑 1: 我忘了在问题中添加userjoinedMessage 方法,这个方法只是为了模仿未来的机制并获得反馈,看看我的解决方案是否真的有效,但这里是:

    def userjoinedMessage(self, pointer):
        message = 
        message["command"] = "STC-userjoinedtest"
        message["message"] = ""
        pointer.send_message(json.dumps(message))

请注意,我试图引用消费者的实例。

我还将尝试实现一个消费者管理器,其职责是跟踪哪些消费者正在浏览哪些项目并将更新发送到相关渠道。

【问题讨论】:

为什么不将项目会话存储在数据库中?这样,如果您决定跨多个服务器扩展您的通道,它就会被持久化,即使跨多个通道服务器实例检索它也不会出现问题 @Ken4scholars 你能详细说明一下吗?我究竟会在数据库中存储什么。请注意,大约 2 个月前我开始对 python、django 和 django 频道完全陌生,所以很多明显的事情对我来说可能还不清楚。 从问题来看,问题是如何保存self.projectSessions 并让它在消费者的多个实例中访问,不是吗?这正是我所说的您可以保存在数据库中的内容。这是一个以项目为键的字典。您可以将其作为 Project 模型的 ForeignKey 的表 @Ken4scholars 我明白你现在的意思,是的,这将是另一种方式。尽管我很难想出一个用例,在这个用例中,我更喜欢在不严格要求时添加数据库所带来的风险。但如果您可以将您的解决方案作为问题的答案,我很乐意接受,感谢您的帮助 【参考方案1】:

从问题来看,问题是如何保存projectSessions 并让它在消费者的多个实例中访问。您可以将其保存在数据库中,而不是尝试将其保存在内存中。它是一个以项目为键的字典。您可以将其作为 Project 模型的 ForeignKey 表。 这样,它就会被持久化,如果您决定跨多个服务器扩展您的通道,即使跨多个通道服务器实例检索它也不会有问题。 另外,如果你觉得传统数据库会拖慢会话的检索速度,那么你可以使用速度更快的存储系统,比如 redis

【讨论】:

【参考方案2】:

是的,这可能是一种可怕的做事方式,我应该被带回去并为此开枪,但我有一个解决我的问题的方法。我创建了一个ProjectManager 类来处理项目用户的订阅和更新:

import json

class ProjectManager():

    def __init__(self):
        if(hasattr(self, 'projectSessions')):
            pass
        else:
            self.projectSessions = 




    def subscribe(self, projectid, consumer):
        print(projectid not in self.projectSessions)
        if(projectid not in self.projectSessions):
            self.projectSessions[projectid] = []
        self.projectSessions[projectid].append(consumer)
        self.update(projectid)


    def unsubscribe(self, projectid, consumer):
        pass

    def update(self, projectid):
        if projectid in self.projectSessions:
            print(self.projectSessions[projectid])
            for consumer in self.projectSessions[projectid]:
                message = 
                message["command"] = "STC-userjoinedtest"
                message["message"] = ""
                consumer.send_message(json.dumps(message))
                pass

在我的apps.py 文件中,我初始化了上面的ProjectManager 类并将其分配给一个变量。

from django.apps import AppConfig
from .manager import ProjectManager


class ProjectConfig(AppConfig):
    name = 'project'
    manager = ProjectManager()

然后我在我的consumers.py 文件中使用它。我从 projectconfig 类导入管理器,并在连接时将其分配给创建的消费者内部的实例变量:

    def connect(self):
        print("connected to projectconsumer...")
        self.accept()
        self.manager = ProjectConfig.manager

每当我打电话给open_project 时,我都会使用从前端收到的给定项目 ID 订阅该项目:

 def open_project(self, message):

        p = Project.objects.values("projectname").get(id=message)
        self.manager.subscribe(message, self)
        message = 
        message["command"] = "STC-openproject"
        message["message"] = p
        self.send_message(json.dumps(message))

正如我所说,我绝不声称这是正确的做法,而且我也知道 channel_layers 应该以一种简洁的方式为您做到这一点。然而,我真的没有时间进入channel_layers,因此将使用它。 当然,我仍然愿意接受建议,并且总是乐于了解更多信息。

【讨论】:

以上是关于我如何订阅消费者并通知他 Django 频道的任何变化的主要内容,如果未能解决你的问题,请参考以下文章

Django 通知用户一个事件并要求一个响应

Django 频道 - websocket_disconnect 在循环中被调用

如何将 Django 频道会话分配给 ORM 中的对象?

UCWA 活动频道适合订阅大量用户吗?

如何在 django 频道中从消费者类外部发送普通 JSON 消息

Django 频道中的消费者