[英]Django-Channels: another chatconsumer that is the same websocket
[英]Contacting another WebSocket server from inside Django Channels
我有兩台 websocket 服務器,分別稱為 Main 和 Worker,這是所需的工作流程:
這是可行的嗎? 我在 Channels 中找不到任何 WS 客戶端功能。 我天真地嘗試這樣做(在consumers.py
中):
import websockets
class SampleConsumer(AsyncWebsocketConsumer):
async def receive(self, text_data):
async with websockets.connect(url) as worker_ws:
await worker_ws.send(json.dumps({ 'to': 'Worker' }))
result = json.loads(await worker_ws.recv())
await self.send(text_data=json.dumps({ 'to': 'Client' })
但是,似乎with
部分會阻塞(在收到來自 Worker 的響應之前,Main 似乎不接受任何進一步的消息)。 我懷疑這是因為websockets
運行自己的循環,但我不確定。 (編輯:我比較id(asyncio.get_running_loop())
,它似乎是同一個循環。我不知道它為什么會阻塞。)
響應{ "to": "Client" }
不需要在這里,即使它使用不同的方法我也可以,只要它在收到來自 Worker 的響應時觸發。
有沒有辦法做到這一點,或者我吠錯了樹?
如果沒有辦法做到這一點,我正在考慮有一個線程(或進程?或單獨的應用程序?)與 Worker 通信,並使用channel_layer
與 Main 通信。 這可行嗎? 如果我能得到確認(對於代碼示例更是如此),我將不勝感激。
編輯我想我知道發生了什么(盡管仍在調查),但是-我相信來自客戶端的一個連接實例化了一個消費者,雖然不同的實例都可以同時運行,但在一個消費者實例中,該實例似乎沒有允許第二種方法開始,直到一種方法完成。 這個對嗎? 現在看看將請求和等待響應代碼移動到線程中是否可行。
我在同一個 position 中,每當我從另一個 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
在上面的代碼中, connect()
充當無限異步迭代器。 更多關於這里。
您可以在自定義管理命令 class 的handle()
方法中運行上述協程。
運行wsclient.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))
最后,運行 manage.py 命令。
python manage.py runwsclient
您還可以將處理程序傳遞給將處理消息的client(ws_url, msg_handler)
,以便處理邏輯將保留在客戶端之外。
2022 年 5 月 31 日更新:
我創建了一個 django package 以將上述功能與最小設置集成: django-websocketclient
是的,Django Channels 不提供 websocket 客戶端,因為它主要用作服務器。 從您的代碼來看,您似乎並不真的需要 Main 和 Worker 之間的 websocket 通信,因為您只需啟動一個套接字,發送一條消息,接收響應並關閉套接字。 這是常規 HTTP 的經典用例,因此如果您真的不需要保持連接處於活動狀態,我建議您使用常規 HTTP 端點並使用 aioHTTP 作為客戶端。
但是,如果您確實需要客戶端,那么您應該在客戶端連接時打開一次套接字,並在客戶端斷開連接時關閉它。 你可以做這樣的事情。
import websockets
async def create_ws(on_create, on_message):
uri = "wss://localhost:8765"
async with websockets.connect(uri) as websocket:
await on_create(websocket)
while True:
message = await websocket.recv()
if message:
await on_message(message)
class WebsocketClient:
asyn def __init__(self, channel):
self.channel = channel
self.ws = None
await creat_ws(self.on_message)
async def on_create(self, was):
self.ws = ws
async def on_message(self, ws, message):
await self.channel.send(text_data=json.dumps(message)
async def send(self, message):
self.ws.send(message)
asunc def close(self):
self.ws.close()
然后在您的消費者中,您可以按如下方式使用客戶端:
class SampleConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.ws_client = WebsocketClient(self)
async def receive(self, text_data):
await self.ws_client.send(text_data)
async def disconnect(self, code):
await self.ws_client.close()
看來我設法使用我發布的最新想法來做到這一點——啟動一個線程來處理與 Worker 的連接。 像這樣的東西:
class SampleConsumer(AsyncWebsocketConsumer):
async def receive(self, text_data):
threading.Thread(
target=asyncio.run,
args=(self.talk_to_worker(
url,
{ 'to': 'Worker' },
),)
).start()
async def talk_to_worker(self, url, message):
async with websockets.connect(url) as worker_ws:
await worker_ws.send(json.dumps(message))
result = json.loads(await worker_ws.recv())
await self.send(text_data=json.dumps({ 'to': 'Client' })
在每個方向上使用 HTTP 請求實際上可能更聰明(因為兩個端點都可以是 HTTP 服務器),但這似乎可行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.