Worker和Websocket(Django和Channels2.x)之间如何使用ChannelNameRouter进行通信?
How to use ChannelNameRouter to communicate between Worker and Websocket (Django and Channels2.x)?
我正在尝试设置一个使用 django2.0.2 和 channels2.1.1 的应用程序。我想要实现的是使用 background/worker 任务来执行一些会产生数据的工作,这些数据应该动态出现在网站上。我的问题主要与渠道相关,是:如何在工作人员和连接到 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,运行服务器控制台输出:
Message to WebsocketConsumer {'type': 'echo_msg', 'msg': 'sent from WebsocketConsumer'}
其中 运行 工作人员控制台输出:
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 向 worker 发送内容,而不是相反?
您的网络套接字从未被使用过。您正在从您的视图向工作人员发送消息 - 而不是从您的消费者向您的工作人员发送消息。当您 post 向视图发送数据时,该视图正在发送该消息。
您在 JavaScript 中的 URL 是 /wspath/
,您在消费者上注册的 URL 是 chat/stream
。 websocket 从不连接。
此外,您的工作人员将根据您当前的设置一次又一次地添加到同一组。您只需要将工作人员添加到组中一次。
如果此回答对您有帮助,请标记为正确。
我正在运行编写您的代码。我可以看到一切正常。
您正在发送初始消息 POST - 正在运行 - 您将其添加到群组中。当 websocket 连接时,它会向 worker 发送一条消息。您可以看到在您的 运行 工作终端中收到了该消息。这会将它弹回给您的消费者,并在您 运行 运行 服务器所在的终端中打印出来。要将其返回到您的网络浏览器,您需要编写:
def echo_msg(self, message):
print("Message to WebsocketConsumer", message)
self.send(json.dumps(message))
在 Chrome 中打开您的开发者工具,看看它会回来。转到网络 > select websocket 连接 > 然后单击框架。
顺便说一句,您不需要一遍又一遍地将测试工作人员添加到同一组。工人总是 运行ning。
另一个顺便说一句:如果您的组对所有用户都具有相同的名称,则您不需要将您的工作人员添加到组中。您可以通过它的路由名称直接向您的工作人员发送消息(发送而不是 group_send)。 worker 可以将消息发送回组,而无需将其添加到组中。您只需要将 websocket 消费者添加到组中。
此外,如果您不希望多个用户看到相同的消息,则根本不需要群组。只需使用频道名称 (self.channel_name) 将消息发送给工作人员,然后将其发送回。
此外,您可能希望与 json 消费者合作,而不是自己解析消息,但这取决于您。
我正在尝试设置一个使用 django2.0.2 和 channels2.1.1 的应用程序。我想要实现的是使用 background/worker 任务来执行一些会产生数据的工作,这些数据应该动态出现在网站上。我的问题主要与渠道相关,是:如何在工作人员和连接到 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,运行服务器控制台输出:
Message to WebsocketConsumer {'type': 'echo_msg', 'msg': 'sent from WebsocketConsumer'}
其中 运行 工作人员控制台输出:
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 向 worker 发送内容,而不是相反?
您的网络套接字从未被使用过。您正在从您的视图向工作人员发送消息 - 而不是从您的消费者向您的工作人员发送消息。当您 post 向视图发送数据时,该视图正在发送该消息。
您在 JavaScript 中的 URL 是 /wspath/
,您在消费者上注册的 URL 是 chat/stream
。 websocket 从不连接。
此外,您的工作人员将根据您当前的设置一次又一次地添加到同一组。您只需要将工作人员添加到组中一次。
如果此回答对您有帮助,请标记为正确。
我正在运行编写您的代码。我可以看到一切正常。
您正在发送初始消息 POST - 正在运行 - 您将其添加到群组中。当 websocket 连接时,它会向 worker 发送一条消息。您可以看到在您的 运行 工作终端中收到了该消息。这会将它弹回给您的消费者,并在您 运行 运行 服务器所在的终端中打印出来。要将其返回到您的网络浏览器,您需要编写:
def echo_msg(self, message):
print("Message to WebsocketConsumer", message)
self.send(json.dumps(message))
在 Chrome 中打开您的开发者工具,看看它会回来。转到网络 > select websocket 连接 > 然后单击框架。
顺便说一句,您不需要一遍又一遍地将测试工作人员添加到同一组。工人总是 运行ning。
另一个顺便说一句:如果您的组对所有用户都具有相同的名称,则您不需要将您的工作人员添加到组中。您可以通过它的路由名称直接向您的工作人员发送消息(发送而不是 group_send)。 worker 可以将消息发送回组,而无需将其添加到组中。您只需要将 websocket 消费者添加到组中。
此外,如果您不希望多个用户看到相同的消息,则根本不需要群组。只需使用频道名称 (self.channel_name) 将消息发送给工作人员,然后将其发送回。
此外,您可能希望与 json 消费者合作,而不是自己解析消息,但这取决于您。