Django 频道页面卡在加载中
Django channels page stuck on loading
我创建了一个连接到 Redis 通道并从该通道接收一些数据的简单 Django Channels 消费者,我想将这些数据发送到前端。
消费者能够连接到消费者并接收数据;问题是,如果我尝试在消费者 运行 时加载页面,页面将卡在加载过程中。我确定发生这种情况是因为与 Redis 通道的连接是一个阻塞操作,或者它可能是线程的问题。我是这个概念的新手,所以我决定为此提出一个问题。
这是我的消费者:
class EchoConsumer(AsyncConsumer):
async def websocket_connect(self, event):
self.send({
'type': 'websocket.accept'
})
self.receive(event)
def receive(self, event):
redis_url = 'redis://localhost:6379/0'
connection = redis.StrictRedis.from_url(redis_url, decode_responses=True)
channel = 'TEST'
params = urllib.parse.parse_qs(self.scope.get('query_string', b'').decode('utf-8'))
pubsub = connection.pubsub(ignore_subscribe_messages=True)
pubsub.subscribe(channel)
for message in pubsub.listen():
# self.send({
# 'type': 'websocket.send',
# 'text': message['data'],
# })
print(message['data'])
async def websocket_disconnect(self, event):
print('DISCONNECTED!')
所以我可以看到打印到我的控制台的数据,但是如果我尝试离开该页面并到达我网站的不同部分,该页面将卡在加载过程中。谁能帮我解决这个问题?
您可以在这里尝试做两件事。
订阅一次并将此信息发送到所有打开的连接
您遇到的问题是 pubsub.listen():
将永远循环(永不停止)。因此您的消费者将永远不会继续并能够处理更多消息。
因为(至少在这个例子中)看起来你总是用静态值访问 redis(不依赖于用户的请求)你最好在你的消费者之外做这个订阅。 (在 django 命令中 https://docs.djangoproject.com/en/3.0/howto/custom-management-commands/)
然后您可以让该命令通过通道层将这些消息发送给您订阅的消费者。
这会让您的消费者看起来像这样
class EchoConsumer(AsyncJsonWebsocketConsumer):
async def on_message(self, message):
await self.send_json(message)
然后在您的管理命令而不是打印中,您可以使用
发送消息
async_to_sync(channel_layer.group_send)(
"echo_group",
{"type": "on.message", "rate":Rate, "quantity": Quantity, "symbol": Symbol, "order": Order},
)
为每个打开的连接订阅一次
只有当您希望每个 websocket 连接的订阅都不同时,才应该这样做。 (例如,您正在使用 url/query/headers 中的值或仅当用户使用给定的过滤器值向您的消费者发送 ws 消息时才订阅)。
这样做 LOT
更复杂,原因如下:
Redis 将不会处理与您拥有的 websocket 连接一样多的打开连接。
您需要设置一个嵌套的异步任务,该任务可以处理来自 redis 的事件,而不会阻塞其余的消费者。
如果您仍然需要此功能,我很乐意用解决方案更新答案(但警告它会很长)。
我创建了一个连接到 Redis 通道并从该通道接收一些数据的简单 Django Channels 消费者,我想将这些数据发送到前端。
消费者能够连接到消费者并接收数据;问题是,如果我尝试在消费者 运行 时加载页面,页面将卡在加载过程中。我确定发生这种情况是因为与 Redis 通道的连接是一个阻塞操作,或者它可能是线程的问题。我是这个概念的新手,所以我决定为此提出一个问题。
这是我的消费者:
class EchoConsumer(AsyncConsumer):
async def websocket_connect(self, event):
self.send({
'type': 'websocket.accept'
})
self.receive(event)
def receive(self, event):
redis_url = 'redis://localhost:6379/0'
connection = redis.StrictRedis.from_url(redis_url, decode_responses=True)
channel = 'TEST'
params = urllib.parse.parse_qs(self.scope.get('query_string', b'').decode('utf-8'))
pubsub = connection.pubsub(ignore_subscribe_messages=True)
pubsub.subscribe(channel)
for message in pubsub.listen():
# self.send({
# 'type': 'websocket.send',
# 'text': message['data'],
# })
print(message['data'])
async def websocket_disconnect(self, event):
print('DISCONNECTED!')
所以我可以看到打印到我的控制台的数据,但是如果我尝试离开该页面并到达我网站的不同部分,该页面将卡在加载过程中。谁能帮我解决这个问题?
您可以在这里尝试做两件事。
订阅一次并将此信息发送到所有打开的连接
您遇到的问题是 pubsub.listen():
将永远循环(永不停止)。因此您的消费者将永远不会继续并能够处理更多消息。
因为(至少在这个例子中)看起来你总是用静态值访问 redis(不依赖于用户的请求)你最好在你的消费者之外做这个订阅。 (在 django 命令中 https://docs.djangoproject.com/en/3.0/howto/custom-management-commands/)
然后您可以让该命令通过通道层将这些消息发送给您订阅的消费者。
这会让您的消费者看起来像这样
class EchoConsumer(AsyncJsonWebsocketConsumer):
async def on_message(self, message):
await self.send_json(message)
然后在您的管理命令而不是打印中,您可以使用
发送消息
async_to_sync(channel_layer.group_send)(
"echo_group",
{"type": "on.message", "rate":Rate, "quantity": Quantity, "symbol": Symbol, "order": Order},
)
为每个打开的连接订阅一次
只有当您希望每个 websocket 连接的订阅都不同时,才应该这样做。 (例如,您正在使用 url/query/headers 中的值或仅当用户使用给定的过滤器值向您的消费者发送 ws 消息时才订阅)。
这样做 LOT
更复杂,原因如下:
Redis 将不会处理与您拥有的 websocket 连接一样多的打开连接。
您需要设置一个嵌套的异步任务,该任务可以处理来自 redis 的事件,而不会阻塞其余的消费者。
如果您仍然需要此功能,我很乐意用解决方案更新答案(但警告它会很长)。