【问题标题】:Multiple concurrent requests in Django async viewsDjango 异步视图中的多个并发请求
【发布时间】:2022-01-23 03:58:20
【问题描述】:

从 3.1 版起,Django 支持异步视图。我有一个在 uvicorn 上运行的 Django 应用程序。我正在尝试编写一个异步视图,它可以同时处理对自身的多个请求,但没有成功。

我见过的常见示例包括从视图内部进行多个慢速 I/O 操作:

async def slow_io(n, result):
    await asyncio.sleep(n)
    return result


async def my_view(request, *args, **kwargs):
    task_1 = asyncio.create_task(slow_io(3, 'Hello'))
    task_2 = asyncio.create_task(slow_io(5, 'World'))
    result = await task_1 + await task_2
    return HttpResponse(result)

这将在 5 秒而不是 8 秒后为我们生成“HelloWorld”,因为请求是同时运行的。

我想要的是同时处理对 my_view 的多个请求。例如。我希望这段代码能在 5 秒内同时处理 2 个请求,但需要 10 秒。

async def slow_io(n, result):
    await asyncio.sleep(n)
    return result

async def my_view(request, *args, **kwargs):
    result = await slow_io(5, 'result')
    return HttpResponse(result)

我用这个命令运行 uvicorn:

uvicorn --host 0.0.0.0 --port 8000 main.asgi:application --reload

Django doc 说:

主要好处是能够在不使用 Python 线程的情况下为数百个连接提供服务。

所以有可能。

我错过了什么?

更新: 看来,我的测试设置是错误的。我在浏览器中打开多个标签并一次刷新它们。详情请见我的answer

【问题讨论】:

  • 如何发送请求?
  • @lucutzu33 我在浏览器中打开多个选项卡并一次全部刷新。这并不精确,但大超时(5、10 秒)足以看出选项卡加载之间的延迟比刷新它们所需的时间长得多。我还添加了时间戳记录,它显示请求之间的准确时间为 5 秒。

标签: django async-await


【解决方案1】:

您的问题是您编写的代码类似于同步版本。你等待每个函数的结果,只有在这之后,你才等待下一个函数。

你只需要使用像gather这样的异步函数来异步运行所有任务:

import asyncio

async def slow_io(n, result):
    await asyncio.sleep(n)
    return result


async def my_view(request, *args, **kwargs):
    all_tasks_result = await asyncio.gather(slow_io(3, 'Hello '), slow_io(5, "World"))
    result = "".join(all_tasks_result)
    return HttpResponse(result)

【讨论】:

  • 请检查我的answer。您描述了问题中的第一个案例,它按预期工作。但我说的是第二种情况 - 视图中的单个异步任务,我必须等待,才能将结果放入响应中。以及对该视图的多个同时请求,我希望同时处理这些请求。它似乎与同步或异步堆栈无关,而是我如何测试它以及浏览器和 Django 的工作方式。
【解决方案2】:

这里是 Django 3.2 上的 sample project,带有多个异步视图和测试。我以多种方式对其进行了测试:

  • 来自 Django 测试客户端的请求按预期同时处理。
  • 按预期同时处理来自单个客户端的对不同视图的请求。
  • 按预期同时处理来自不同客户端的对同一视图的请求。

什么没有按预期工作?

  • 单个客户端对同一视图的请求一次由一个客户端处理,我没想到会这样。

Django中有一个警告doc

只有在您的站点中没有加载同步中间件时,您才能获得完全异步请求堆栈的好处。如果有一个同步中间件,那么 Django 必须为每个线程使用一个线程请求为其安全地模拟同步环境。

可以构建中间件来支持同步和异步上下文。 Django 的一些中间件是这样构建的,但不是全部。 要查看 Django 必须适应哪些中间件,您可以打开 django.request 记录器的调试日志记录并查找有关“同步中间件……已适应”的日志消息.

因此,即使在裸 Django 中,也可能是一些同步中间件导致问题。但它也指出 Django 在这种情况下应该使用线程,而我没有使用任何同步中间件,只使用标准的。

我的最佳猜测是,它与客户端-服务器连接有关,与同步或异步堆栈无关。尽管 Google 表示出于安全原因,浏览器会为每个选项卡创建新连接,但我认为:

  • 如果出于经济原因,URL 相同,浏览器可能会为多个选项卡保持相同的连接。
  • 如它所述,Django 会根据连接而不是根据请求创建异步任务或线程。

需要检查一下。

【讨论】:

    猜你喜欢
    • 2011-01-08
    • 2015-06-09
    • 2015-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-26
    • 2018-04-10
    • 1970-01-01
    相关资源
    最近更新 更多