从 Django Channels 内部联系另一个 WebSocket 服务器

Ama*_*dan 8 python django websocket python-asyncio django-channels

我有两个 websocket 服务器,分别称为 Main 和 Worker,这是所需的工作流程:

\n
    \n
  • 客户端向Main发送消息
  • \n
  • Main向Worker发送消息
  • \n
  • Worker 响应 Main
  • \n
  • 主要响应客户端
  • \n
\n

这可行吗?我在 Channels 中找不到任何 WS 客户端功能。我天真地尝试这样做(在consumers.py):

\n
import websockets\n\nclass SampleConsumer(AsyncWebsocketConsumer):\n    async def receive(self, text_data):\n        async with websockets.connect(url) as worker_ws:\n            await worker_ws.send(json.dumps({ 'to': 'Worker' }))\n            result = json.loads(await worker_ws.recv())\n        await self.send(text_data=json.dumps({ 'to': 'Client' })\n
Run Code Online (Sandbox Code Playgroud)\n

然而,似乎该with部分被阻止(Main 似乎不接受任何进一步的消息,直到收到 Worker 的响应)。我怀疑这是因为websockets运行了自己的循环,但我不确定。(编辑:我比较了一下id(asyncio.get_running_loop()),它似乎是相同的循环。我不知道为什么它会阻塞。)

\n

响应{ "to": "Client" }不需要在这里,即使它是在不同的方法中,我也可以,只要它在收到 Worker 的响应时触发即可。

\n

有没有办法做到这一点,还是我找错了树?

\n

如果没有办法做到这一点,我正在考虑有一个线程(或进程?或单独的应用程序?)与 Worker 通信,并用于channel_layer与 Main 通信。这可行吗?如果我能得到确认(对于代码示例更是如此),我将不胜感激。

\n

编辑我想我明白发生了什么(尽管仍在调查),但是 \xe2\x80\x94 我相信来自客户端的一个连接实例化了一个消费者,虽然不同的实例都可以同时运行,但在一个消费者实例中似乎在一个方法完成之前,该实例不允许启动第二个方法。它是否正确?现在看看将请求和等待响应代码移动到线程中是否可行。

\n

Sco*_*onk 6

每当我从另一个 WebSocket 服务器收到消息时,我都想在 Django 应用程序中处理消息。

我的想法是使用WebSockets客户端库,并使用Django 论坛上这篇manage.py文章中的命令将其作为单独的进程运行。

您可以定义一个异步协程client(websocket_url)来侦听从 WebSocket 服务器接收的消息。

import asyncio
import websockets


async def client(websocket_url):
    async for websocket in websockets.connect(uri):
        print("Connected to Websocket server")
        try:
            async for message in websocket:
            # Process message received on the connection.
                print(message)
        except websockets.ConnectionClosed:
            print("Connection lost! Retrying..")
            continue #continue will retry websocket connection by exponential back off 
Run Code Online (Sandbox Code Playgroud)

在上面的代码中connect()充当无限异步迭代器。更多内容请参见此处

handle()您可以在自定义管理命令类的方法内运行上述协程。

runwsclient.py

from django.core.management.base import BaseCommand

class Command(BaseCommand):

    def handle(self, *args, **options):
        URL = "ws://example.com/messages"
        print(f"Connecting to websocket server {URL}")
        asyncio.run(client(URL))
Run Code Online (Sandbox Code Playgroud)

最后,运行manage.py命令。

python manage.py runwsclient

您还可以将处理消息传递给client(ws_url, msg_handler)它,以便处理逻辑将保留在客户端之外。

更新 31/05/2022

我创建了一个 django 包,以最少的设置集成上述功能:django-websocketclient