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

Я пытаюсь настроить приложение, которое использует django2.0.2 и channel2.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.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

и затем вызвать работника, консоль runserver выводит:

Сообщение в WebsocketConsumer {'type': 'echo_msg', 'msg': 'отправлено из WebsocketConsumer'}

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

Сообщение работнику {'type': 'echo_msg', 'msg': 'sent from worker'}

Сообщение работнику {'type': 'echo_msg', 'msg': 'отправлено из WebsocketConsumer'}

Так что я могу отправить материал работника -> работник, WebsocketConsumer -> WebsocketConsumer, WebsocketConsumer -> работник.

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

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

Ответы [ 2 ]

0 голосов
/ 08 мая 2018

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

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

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

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

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

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

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

Также вам, вероятно, захочется работать с потребителями json, а не разбирать сообщения самостоятельно, но это ваше дело.

0 голосов
/ 06 мая 2018

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

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

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

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

...