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