【问题标题】:How long does the event_loop live in a Django>=3.1 async viewevent_loop 在 Django>=3.1 异步视图中存在多长时间
【发布时间】:2020-12-10 13:33:28
【问题描述】:

我正在使用 Django 3.1 中的新异步视图。

我希望获得的一些好处是在视图已经提供其HttpResponse 之后执行一些简单的即发即弃的“任务”,例如发送推送通知或发送电子邮件。 我不是在寻找像 celery 这样的第三方包的解决方案!

为了测试这个异步视图,我使用了本教程中的一些代码:https://testdriven.io/blog/django-async-views/

async def http_call_async():
    for num in range(1, 200):
        await asyncio.sleep(1)
        print(num)
        
    # TODO: send email async
    print('done')


async def async_view(request):
    loop = asyncio.get_event_loop()
    loop.create_task(http_call_async())
    return HttpResponse("Non-blocking HTTP request")

我用 uvicorn 启动了 django 服务器。

当我向这个视图发出请求时,它会立即返回 HTTP 响应“非阻塞 HTTP 请求”。

与此同时,在 HTTP 响应之后,事件循环继续愉快地打印最多 200 的数字,然后打印“完成”。

这正是我想要用于我的即发即弃任务的行为。

很遗憾,我找不到有关运行此代码的事件循环的生命周期的任何信息。

事件循环的生命周期是多久? 有超时吗? 它取决于什么?在uvicorn上?这是可配置的吗?

有没有讨论这个话题的资源?

【问题讨论】:

    标签: python django python-asyncio


    【解决方案1】:

    Django(不提供 ASGI 服务器)不限制事件循环的生命周期。

    在这个问题的情况下:

    • ASGI 服务器:Uvicorn
    • 事件循环:内置 asyncio 事件循环或uvloop(均不限制其生命周期)

    对于 Uvicorn:

    • 事件循环的生命周期有多长?
      事件循环的生命周期与工作线程的生命周期一样长。

    • 有超时吗?
      不,任务一直运行到完成(除非工作人员没有响应并被 Gunicorn 杀死)。

    • 它取决于什么?在 Uvicorn 上?
      Worker 的生命周期主要受限于--limit-max-requests(Gunicorn:--max-requests)。

      • 这是可配置的吗?
        是的,但工人仍然会在某个时候退出。它也可能因其他原因被杀死。

    通过指定--limit-max-requests 2 并运行两次视图来亲自查看问题:

    uvicorn mysite.asgi:application --limit-max-requests 2
    

    优雅关机

    不管生命周期如何限制,我们可能关心的是如何处理关机。

    Uvicorn 如何优雅关机?

    让我们看看 Uvicorn worker (uvicorn.server.Server) 是如何优雅关闭请求的。

    uvicorn.protocols.http.h11_impl.H11Protocol#__init__:存储对服务器任务的引用

    # Shared server state
    self.server_state = server_state
    self.tasks = server_state.tasks
    

    uvicorn.protocols.http.h11_impl.H11Protocol#handle_events:将请求任务添加到任务中

    self.cycle = RequestResponseCycle(
        ...
    )
    task = self.loop.create_task(self.cycle.run_asgi(app))
    task.add_done_callback(self.tasks.discard)
    self.tasks.add(task)
    

    uvicorn.server.Server#shutdown:等待现有任务完成

    if self.server_state.tasks and not self.force_exit:
        msg = "Waiting for background tasks to complete. (CTRL+C to force quit)"
        logger.info(msg)
        while self.server_state.tasks and not self.force_exit:
            await asyncio.sleep(0.1)
    

    如何让 Uvicorn worker 等待你的任务?

    让我们通过将您的任务添加到服务器任务来搭载它。

    async def async_view(request):
        loop = asyncio.get_event_loop()
        # loop.create_task(http_call_async())       # Replace this
        task = loop.create_task(http_call_async())  # with this
        server_state = await get_server_state()             # Add this
        task.add_done_callback(server_state.tasks.discard)  # Add this
        server_state.tasks.add(task)                        # Add this
        return HttpResponse("Non-blocking HTTP request")
    
    import gc
    from uvicorn.server import ServerState
    
    _server_state = None
    
    
    @sync_to_async
    def get_server_state():
        global _server_state
        if not _server_state:
            objects = gc.get_objects()
            _server_state = next(o for o in objects if isinstance(o, ServerState))
        return _server_state
    

    现在,Uvicorn 工作人员将在正常关机中等待您的任务。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-09
      • 2020-10-23
      • 2020-12-01
      • 2020-12-31
      • 1970-01-01
      • 2021-01-16
      • 2022-01-23
      • 1970-01-01
      相关资源
      最近更新 更多