我需要推迟发送频道消息。这是我的代码:
# consumers.py
class ChatConsumer(WebsocketConsumer):
def chat_message(self, event):
self.send(text_data=json.dumps(event['message']))
def connect(self):
self.channel_layer.group_add(self.room_name, self.channel_name)
self.accept()
def receive(self, text_data=None, bytes_data=None):
send_message_task.apply_async(
args=(
self.room_name,
{'type': 'chat_message',
'message': 'the message'}
),
countdown=10
)
# tasks.py
@shared_task
def send_message_task(room_name, message):
layer = get_channel_layer()
layer.group_send(room_name, message)
该任务正在执行,我看不到任何错误,但消息未发送。仅当我从消费者类方法发送它时,它才起作用。
我也尝试使用AsyncWebsocketConsumer并使用AsyncToSync(layer.group_send)发送。错误为“您不能在与异步事件循环相同的线程中使用AsyncToSync,请直接等待异步功能。”
然后我尝试将send_message_task声明为异步并使用await。什么也没有发生(没有错误),而且我不确定该任务是否已执行。
以下是版本:
Django==1.11.13
redis==2.10.5
django-celery==3.2.2
channels==2.1.2
channels_redis==2.2.1
设置:
REDIS_HOST = os.getenv('REDIS_HOST', '127.0.0.1')
BROKER_URL = 'redis://{}:6379/0'.format(REDIS_HOST)
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": ['redis://{}:6379/1'.format(REDIS_HOST)],
},
},
}
有什么想法吗?
UPD::刚刚发现redis通道层被撤消,但是它的group_send方法没有被调用而是被跳过。
UPD 2:使用控制台上的AsyncToSync(layer.group_send)
发送。没有apply_async
的调用任务也可以。但是使用apply_async
运行它会导致错误You cannot use AsyncToSync in the same thread as an async event loop - just await the async function directly
。将任务定义为异步并使用await
当然也会破坏一切。
答案 0 :(得分:2)
使用通道层时,在连接时需要async_to_sync()
包装器,因为所有通道层方法都是异步的。
def connect(self):
async_to_sync(self.channel_layer.group_add(
self.room_name, self.channel_name)
self.accept()
从您的芹菜任务发送消息的方式相同。
@shared_task
def send_message_task(room_name, message):
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
room_name,
{'type': 'chat_message', 'message': message}
)
您也可以像这样从消费者的receive()
调用您的芹菜任务:
send_message_task.delay(self.room_name, 'your message here')
关于AsyncToSync错误,您需要将频道和daphne升级为explained in this thread的较新版本。
答案 1 :(得分:1)
我发现了一个丑陋且效率低下的决定,但它有效:
@shared_task
def send_message_task(room_name, message):
def sender(room_name, message):
channel_layer = get_channel_layer()
AsyncToSync(channel_layer.group_send)(
room_name,
{'type': 'chat_message', 'message': message}
)
thread = threading.Thread(target=sender, args=(room_name, message,))
thread.start()
thread.join()
如果有人可以改善它,我将不胜感激。
答案 2 :(得分:0)
代码中的问题是您在类型underscore
中使用了chat_message
。我相信您在文档中错过了它:
方法的名称将是带有句点的事件的类型 由下划线代替-例如,在 类型为
chat.join
的频道层将由方法处理chat_join
。
因此,在您的情况下,类型将为chat.message
{
'type': 'chat.message',
'message': 'the message'
}
答案 3 :(得分:0)
也许这不是开始问题的直接答案,但这可能会有所帮助。 如果您收到异常消息“您不能在与异步事件循环相同的线程中使用AsyncToSync-请直接等待异步函数”,那么您可能会这样做:
AsyncToSync似乎检测到外部事件循环并做出不干扰它的决定。
解决方案是将异步调用直接包含在外部事件循环中。 下面是示例代码,但是最好是检查您的情况并确定外部循环正在运行...
loop = asyncio.get_event_loop()
loop.create_task(layer.group_send(room_name, {'type': 'chat_message', 'message': message}))