【问题标题】:How to use ChannelNameRouter to communicate between Worker and Websocket (Django and Channels2.x)?如何使用 ChannelNameRouter 在 Worker 和 Websocket(Django 和 Channels2.x)之间进行通信?
【发布时间】:2018-10-16 08:51:50
【问题描述】:

我正在尝试设置一个使用 django2.0.2 和 channels2.1.1 的应用程序。我想要实现的是使用后台/工作任务来执行一些会产生数据的工作,这些数据应该动态地出现在网站上。我的问题,主要与渠道有关,是:如何正确地在工作人员和连接到 websocket 的消费者之间建立通信?

下面是一个突出问题的最小示例:这个想法是用户触发工作器,工作器产生一些数据并通过通道层将其发送到连接到 websocket 的消费者。

#routing.py
from channels.routing import ChannelNameRouter, ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from django.urls import path
from testApp.consumers import *

application = ProtocolTypeRouter({
    "websocket":AuthMiddlewareStack(
        URLRouter([
            path("wspath",TestConsumer),
        ]),
    ),
    "channel":ChannelNameRouter({
        "test_worker": TestWorker,
    }),
})

消费者:

#consumers.py
from channels.consumer import SyncConsumer
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync

class TestConsumer(WebsocketConsumer):
    def websocket_connect(self,message):
        async_to_sync(self.channel_layer.group_add)("testGroup",self.channel_name)
        self.connect()
        #I understand this next part is a bit weird, but I figured it 
        #is the most concise way to explain my problem
        async_to_sync(self.channel_layer.group_send)(
            "testGroup",
            {
                'type':"echo_msg",
                'msg':"sent from WebsocketConsumer",
            })

    def echo_msg(self, message):
        print("Message to WebsocketConsumer", message)

class TestWorker(SyncConsumer):
    def triggerWorker(self, message):
        async_to_sync(self.channel_layer.group_add)("testGroup",self.channel_name)
        async_to_sync(self.channel_layer.group_send)(
            "testGroup",
            {
                'type':"echo_msg",
                'msg':"sent from worker",
            })

    def echo_msg(self, message):
        print("Message to worker ", message)

景色

#views.py
from django.shortcuts import render
import channels.layers
from asgiref.sync import async_to_sync

def index(request):
    if request.method == "POST":
        channel_layer = channels.layers.get_channel_layer()
        async_to_sync(channel_layer.send)('test_worker',{
            'type':'triggerWorker',
        })
    return render(
        request,
        "index.html",
        {})

和html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <script>
        console.log('ws://' + window.location.host)
        var socket = new WebSocket(
            'ws://' + window.location.host + "/wspath"
        );
    </script>
</head>
<div>Click to run worker</div>
<body>
    <form action="" method="POST">
        {% csrf_token %}
        <button type="submit">Start</button>   
    </form>
</body>

现在,当我通过执行(在单独的控制台中)运行它时

python3 manage.py runserver

python3 manage.py runworker test_worker

然后触发worker,runserver控制台输出:

给 WebsocketConsumer 的消息 {'type': 'echo_msg', 'msg': 'sent from WebsocketConsumer'}

runworker 控制台输出的位置:

给工人的消息 {'type': 'echo_msg', 'msg': 'sent from worker'}

给工作人员的消息 {'type': 'echo_msg', 'msg': 'sent from WebsocketConsumer'}

所以我可以发送东西 worker -> worker, WebsocketConsumer -> WebsocketConsumer, WebsocketConsumer -> worker。

据我了解(这显然是错误的)应该还有一个消息工作者 -> WebsocketConsumer,因为我将两者都添加到了“testGroup”中。

所以,我的问题是为什么 WebsocketConsumer 没有从工作人员那里收到任何东西(这是我感兴趣的,最终与 javaScript 建立通信)?或者换句话说,为什么我只能从 WebsocketConsumer 向 worker 发送东西,反之则不行?

【问题讨论】:

    标签: django python-3.x websocket django-channels


    【解决方案1】:

    我正在运行您的代码。我可以看到一切正常。

    您在 POST 上收到了初始消息 - 这是有效的 - 您将其添加到组中。当 websocket 连接时,它会向工作人员发送一条消息。您可以看到在您的 runworker 终端中收到了该信息。这会将其反弹回您的消费者,并在您运行 runserver 的终端中打印出来。要将其返回到您的网络浏览器,您需要编写:

    def echo_msg(self, message): print("Message to WebsocketConsumer", message) self.send(json.dumps(message))

    在 Chrome 中打开您的开发者工具以查看它的恢复情况。转到网络 > 选择 websocket 连接 > 然后单击框架。

    顺便说一句,您不需要一遍又一遍地将测试工作者添加到同一个组中。工人总是在运行。

    另一个顺便说一句:如果要为所有用户命名相同的组,则无需将工作人员添加到组中。您可以通过路由名称(发送而不是 group_send)直接向您的工作人员发送消息。工作人员可以将消息发送回组,而无需将其添加到组中。您只需要将 websocket 消费者添加到组中。

    此外,如果您不希望多个用户看到相同的消息,则根本不需要群组。只需将消息发送给具有通道名称(self.channel_name)的工作人员即可将其发送回。

    此外,您可能希望与 json 消费者一起工作,而不是自己解析消息,但这取决于您。

    【讨论】:

    • 非常感谢。我已经让它工作了。也谢谢你澄清这些点,显然我对频道中的一些概念的理解仍然有点不稳定。
    【解决方案2】:

    您的 websocket 从未被使用过。您正在从您的视图向工作人员发送消息 - 而不是从您的消费者发送给您的工作人员。当您向其发布数据时,该视图正在发送该消息。

    您的 JavaScript 中的 URL 是 /wspath/,而您在消费者上注册的 URL 是 chat/stream。 websocket 从不连接。

    此外,您的工作人员将使用您当前的设置一次又一次地添加到同一个组中。您只需将工作人员添加到组一次。

    如果此答案对您有帮助,请标记为正确。

    【讨论】:

    • 我删掉了那个相当愚蠢的错误。但无论如何,经过纠正,我观察到相同的行为
    猜你喜欢
    • 1970-01-01
    • 2021-10-16
    • 1970-01-01
    • 2021-05-24
    • 2010-12-12
    • 1970-01-01
    • 1970-01-01
    • 2016-06-07
    • 1970-01-01
    相关资源
    最近更新 更多