避免 Django 频道的"Two event loops are trying to receive() on one channel layer at once!"错误



简而言之:我正试图让Django通道的消费者使用一个单独的逻辑线程进行通信。

不管我在下面问什么,除了Django频道之外,可能还有一个更适合我的选择。我的目标是:

  • 我想以编程方式启动逻辑线程,这就是为什么Django Channels工作线程不适合我的原因
  • 我希望逻辑线程能够向消费者发送数据
  • 我希望消费者能够将数据发送到逻辑线程
  • 我不想在(例如("游戏"中处理游戏逻辑;主机";消费者,因为如果主机必须重新连接他们的网络套接字,游戏就会崩溃

更长的版本:假设我正在尝试在一个网站上制作一个简单的游戏,它使用websocket在前端和后端之间进行通信。使用Django通道,这可以非常容易地设置。

然而,当我需要处理游戏逻辑时,我想在某个中心线程中计算它,该线程在客户端断开连接时不会停止。我发现,您可以从消费者范围之外的任意线程与消费者进行通信。然而,这有一个问题:

  • 游戏逻辑可以很容易地向客户端发送信息,但客户端无法发回信息
  • 如果我们为客户端使用RESTful API来向游戏逻辑线程发送信息,这将是缓慢的,并且我们不能使用consumer.disconnect()来干净地处理客户端断开连接
  • 当尝试在游戏逻辑线程中使用async_to_sync(self.channel_layer.receive)(...)时,会导致以下错误:Two event loops are trying to receive() on one channel layer at once!很明显,RedisChannelLayer不喜欢单独的线程侦听事件

因此,我认为我需要找到另一种方式,以某种方式将信息从消费者发送到其他线程。换句话说,在以下代码片段中:

class GameClientConsumer(JsonWebsocketConsumer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def connect(self):
# Accept the websocket connection.
self.accept()
def receive_json(self, content=None, **kwargs):
# Handle player input
send_information_to_another_thread(data)

我需要找到send_information_to_another_thread(data)的样子。这可能吗?或者,我想知道如何从任意线程中以某种方式从通道层receive()事件。

根据您的解释,您似乎需要两件事:

  1. 从消费者外部向通道层发送消息。正如你所说,你已经在文档中找到了这一点。

  2. 从使用者向另一个线程或代码块发送消息。使用者代码就像任何其他Python代码一样,因此您可以从中调用任意函数。当然,您应该注意不要阻塞事件循环,因此应该将使用者之外的重逻辑分配给另一个进程或线程。您可以将外部逻辑放在一个芹菜任务中,例如,如果它很重,只需像调用任何其他芹菜任务一样调用该任务。本质上:

    def receive_json(self, content=None, **kwargs):
    # Handle player input
    celery_task.delay(**kwargs)
    

这样,每次收到新消息时,任务都会运行。如果你有很多这样的任务,你可以在收到新消息时触发Django信号,然后处理程序可以决定如何处理它(触发一个芹菜任务,运行一个小逻辑并返回,向消费者发回一些消息,等等(

您不需要通过通道层调用receive函数。相反,将消息从接收功能推送到需要它们的服务。

相关内容

最新更新