django.channels异步使用者似乎不是异步执行的

时间:2018-02-19 18:04:42

标签: django python-asyncio django-channels

我已将django.channels添加到django项目中,以支持长时间运行的进程,通过websockets通知用户进度。

除了长时间运行的流程的实现似乎不是异步响应之外,一切似乎都能正常工作。

为了进行测试,我创建了一个AsyncConsumer来识别两种类型的消息'运行'和' isBusy'。

'''消息处理程序设置一个忙标志'发回一个'进程正在运行'消息,等待异步 20秒,重置“忙碌标志”'然后发回一个'流程完成消息'

' isBusy' message返回一条带有忙标志状态的消息。

我的期望是,如果我发送一条运行消息,我会立即收到一个'进程正在运行'回复消息,20秒后我将收到一个完整的'信息。 这可以按预期工作。

我也希望如果我发送一个' isBusy'消息我将立即收到有关旗帜状态的回复。

观察到的行为如下:

  • 消息'运行'发送(来自客户)
  • 一条消息'正在运行请等待'立即收到
  • 消息' isBusy'发送(来自客户)
  • 消息到达服务器端的Web套接字侦听器
  • 没有任何反应,直到运行处理程序完成
  • a'跑完了'客户收到消息
  • 紧接着一个'进程isBusy:False'消息

以下是Channel侦听器的实现:

class BackgroundConsoleConsumer(AsyncConsumer):
    def __init__(self, scope):
        super().__init__(scope)
        self.busy = False

    async def run(self, message):
        print("run got message", message)
        self.busy = True
        await self.channel_layer.group_send('consoleChannel',{
                    "type":"consoleResponse",
                    "text":"running please wait"
                })
        await asyncio.sleep(20)
        self.busy = False
        await self.channel_layer.group_send('consoleChannel',{
                    "type":"consoleResponse",
                    "text": "finished running"
                })

    async def isBusy(self,message):
        print('isBusy got message', message)
        await self.channel_layer.group_send('consoleChannel',{
                    "type":"consoleResponse",
                    "text":  "process isBusy:{0}".format(self.busy)
                })

通道在路由文件中设置如下:

application = ProtocolTypeRouter({
    "websocket": AuthMiddlewareStack(
        URLRouter([
            url("^console/$", ConsoleConsumer),
        ])

    ),
    "channel": ChannelNameRouter({
        "background-console":BackgroundConsoleConsumer,
    }),
})

我使用一名工作人员(通过./manage.py runworker)运行频道。

实验是使用django测试服务器(通过runserver)完成的。

关于为什么频道消费者似乎不是异步工作的任何想法都将不胜感激。

1 个答案:

答案 0 :(得分:4)

在这里进行了一些挖掘之后是问题和解决方案。

频道会将发送给它的消息添加到asyncio.Queue并按顺序处理它们。

仅释放协程控件(通过asyncio.sleep()或类似的东西)是不够的,必须在消费者收到新消息之前完成处理消息处理程序。

以上示例的修复程序按预期运行(即在处理isBusy长时间运行的任务时响应run消息)

感谢@ user4815162342获取您的建议。

class BackgroundConsoleConsumer(AsyncConsumer):
    def __init__(self, scope):
        super().__init__(scope)
        self.busy = False

    async def run(self, message):
        loop = asyncio.get_event_loop()
        loop.create_task(self.longRunning())

    async def longRunning(self):
        self.busy = True
        await self.channel_layer.group_send('consoleChannel',{
                    "type":"the.type",
                    "text": json.dumps({'message': "running please wait", 'author': 'background console process'})
                })
        print('before sleeping')
        await asyncio.sleep(20)
        print('after sleeping')
        self.busy = False
        await self.channel_layer.group_send('consoleChannel',{
                    "type":"the.type",
                    "text": json.dumps({'message': "finished running", 'author': 'background console process'})
                })

    async def isBusy(self,message):
        print('isBusy got message', message)
        await self.channel_layer.group_send('consoleChannel',{
                    "type":"the.type",
                    "text":  json.dumps({'message': "process isBusy:{0}".format(self.busy),
                                         'author': 'background console process'})
                })