Как использовать ChannelNameRouter для связи между Worker и Websocket (Django и Channels2.x)?

Я пытаюсь настроить приложение, использующее django2.0.2 и channels2.1.1. Я хотел бы достичь использования фоновой / рабочей задачи для выполнения некоторой работы, которая будет производить данные, которые должны динамически появляться на веб-сайте. Моя проблема, связанная в первую очередь с каналами, заключается в следующем: как мне правильно установить связь между исполнителем и потребителем, подключенным к веб-сокету?

Ниже приведен минимальный пример, подчеркивающий проблему: идея состоит в том, что пользователь запускает воркера, воркер создает некоторые данные и отправляет их через канальный уровень потребителю, который подключен к веб-сокету.

#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.info('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

а затем запускайте рабочий, консоль сервера запуска выводит:

Message to WebsocketConsumer {'type': 'echo_msg', 'msg': 'sent from WebsocketConsumer'}

где как выводит консоль runworker:

Message to worker {'type': 'echo_msg', 'msg': 'sent from worker'}

Message to worker {'type': 'echo_msg', 'msg': 'sent from WebsocketConsumer'}

Итак, я могу отправить материал worker -> worker, WebsocketConsumer -> WebsocketConsumer, WebsocketConsumer -> worker.

Насколько я понимаю (что, очевидно, неверно), также должен был быть работник сообщения -> WebsocketConsumer, потому что я добавил и то, и другое в "testGroup".

Итак, у меня вопрос: почему WebsocketConsumer ничего не получил от рабочего (это то, что меня интересует, чтобы в конечном итоге установить связь с javaScript)? Или, другими словами, почему я могу отправлять материал только из WebsocketConsumer рабочему, а не наоборот?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
0
1 746
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ваш веб-сокет никогда не используется. Вы отправляете сообщение со своего представления работнику, а не от потребителя к работнику. Представление отправляет это сообщение, когда вы отправляете ему данные.

URL-адрес, который у вас есть в вашем JavaScript, - /wspath/, а URL-адрес, который вы зарегистрировали на своем потребителе, - chat/stream. Веб-сокет никогда не подключается.

Кроме того, ваш рабочий будет добавлен в одну и ту же группу снова и снова с вашей текущей настройкой. Вам нужно всего лишь один раз добавить воркера в группу.

Если этот ответ помог вам, отметьте его правильным.

Я исправил эту довольно глупую ошибку. Но в любом случае, после исправления я наблюдаю то же поведение

Ziegl 07.05.2018 20:21
Ответ принят как подходящий

Я запускаю ваш код. Я вижу, что все работает.

У вас есть начальное сообщение, отправляемое в POST - оно работает - вы добавляете его в группу. Когда веб-сокет подключается, он отправляет сообщение работнику. Вы можете видеть это в своем терминале runworker. Это возвращает его вашему потребителю и распечатывается в терминале, на котором вы запускаете runserver. Чтобы вернуть его в свой веб-браузер, вам нужно написать:

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

Откройте инструменты разработчика в Chrome, чтобы увидеть, как он вернется. Перейдите в сеть> выберите подключение к веб-сокету> затем щелкните фреймы.

Кстати, вам не нужно снова и снова добавлять тестировщика в одну и ту же группу. Рабочий всегда работает.

Еще один кстати: если ваши группы будут называться одинаково для всех пользователей, вам не нужно добавлять своего работника в группу. Вы можете отправлять сообщения прямо своему воркеру по его имени маршрута (send вместо group_send). Рабочий может отправлять сообщения обратно в группу без добавления в группу. Вам нужно только добавить потребителей веб-сокетов в группы.

Кроме того, если вы не хотите, чтобы несколько пользователей видели одни и те же сообщения, вам вообще не нужны группы. Просто отправьте сообщение работнику с именем канала (self.channel_name), чтобы отправить его обратно.

Кроме того, вы, вероятно, захотите работать с потребителями json, а не разбирать сообщения самостоятельно, но это зависит от вас.

Большое спасибо. Теперь у меня все работает. Также благодарю за прояснение этих моментов, ясно, что мое понимание некоторых концепций в каналах все еще немного шаткое.

Ziegl 08.05.2018 10:51

Другие вопросы по теме