Web | 是时候试试Django 3.1新的异步视图功能了
WEB前端开发社区 昨天
编写异步视图(async views)使你能够毫不费力地加速你的应用程序。随着Django 3.1最终支持异步视图,异步中间件和测试,现在是学习使用它的好时机。这篇文章探讨了如何开始使用Django 3.1提供的新异步视图。
先决条件
如果你Django已经比较熟悉,那么在基于函数的视图中添加异步功能将变得非常直接简单。
本项目环境依赖
Python >= 3.8
Django >= 3.1
Uvicorn
HTTPX
什么是ASGI?
ASGI代表异步服务器网关接口。这是继WSGI以后一个现代的支持异步的服务器网关接口,为创建基于Python的异步Web应用程序提供了标准。
值得一提的另一件事是,ASGI与WSGI向后兼容的,即使你不准备转向编写异步应用程序,也可以将其从Gunicorn或uWSGI之类的WSGI服务器切换至Uvicorn或Daphne之类的ASGI服务器。
创建项目与应用
创建一个新的项目目录以及一个新的Django项目:
$ mkdir django-async-views && cd django-async-views$ python3.8 -m venv env$ source env/bin/activate (env)$ pip install django(env)$ django-admin.py startproject hello_async .
你可以随意将virtualenv和Pip换成Poetry或Pipenv。
如果您使用Django内置开发测试服务器,你的项目可以启动,但实际上它不会真正异步运行它们,因此我们将使用Uvicorn来启动你的项目。
安装它:
(env)$ pip install uvicorn
要使用Uvicorn运行项目,请从项目的根目录使用以下命令:
uvicorn {name of your project}.asgi:application
在本例中,这将是:
(env)$ uvicorn hello_async.asgi:application
接下来,让我们创建第一个异步视图。添加一个新文件以将视图保存在“hello_async”文件夹中,然后添加以下视图:
from django.http import HttpResponse async def index(request): return HttpResponse("Hello, async Django!")
在Django中创建异步视图函数就像创建同步视图函数一样简单-您只需要在前面添加async
关键字即可。
更新urls.py:
from django.contrib import adminfrom django.urls import path from hello_async.views import index urlpatterns = [ path("admin/", admin.site.urls), path("", index),]
现在,在根文件夹的终端中运行如下命令:
(env)$ uvicorn hello_async.asgi:application --reload
该--reload
标志告诉uvicorn监视文件中的更改,如果发现更改,则重新加载。
现在打开你的浏览器,访问http://localhost:8000/,你将看到:
Hello, async Django!
这不是世界上最令人兴奋的事情,但是,嘿,这是一个开始。值得注意的是,使用Django的内置开发服务器运行此视图将获得完全相同的功能和输出。这是因为我们实际上没有在处理程序中执行任何异步操作。
异步视图中执行异步任务会发生什么?
值得注意的是,异步支持是完全向后兼容的,因此您可以混合使用异步和同步视图,中间件和测试。Django将在适当的执行上下文中执行每个操作。
为了说明这一点,请添加一些新视图,如下所示。我们分别创建了一个异步和同步的任务,然后在异步视图和同步视图中调用它们。
import asynciofrom time import sleep import httpxfrom django.http import HttpResponse # 异步任务async def http_call_async(): for num in range(1, 6): await asyncio.sleep(1) print(num) async with httpx.AsyncClient() as client: r = await client.get("https://httpbin.org/") print(r) # 同步任务def http_call_sync(): for num in range(1, 6): sleep(1) print(num) r = httpx.get("https://httpbin.org/") print(r) # 异步视图 - 无异步或同步任务async def index(request): return HttpResponse("Hello, async Django!") # 异步视图 - 调用异步任务async def async_view(request): loop = asyncio.get_event_loop() loop.create_task(http_call_async()) return HttpResponse("Non-blocking HTTP request") # 同步视图 - 调用普通同步任务def sync_view(request): http_call_sync() return HttpResponse("Blocking HTTP request")
更新urls.py:
# hello_async/urls.py from django.contrib import adminfrom django.urls import path from hello_async.views import index, async_view, sync_view urlpatterns = [ path("admin/", admin.site.urls), path("async/", async_view), path("sync/", sync_view), path("", index),]
由于我们的异步任务里使用了httpx库,所以需要安装HTTPX:
(env)$ pip install httpx
在服务器运行的情况下,浏览器访问http://localhost:8000/async/, 您应该立即看到如下响应:
Non-blocking HTTP request
在您的终端中,您应该看到:
INFO: 127.0.0.1:60374 - "GET /async/ HTTP/1.1" 200 OK12345<Response [200 OK]>
在这个异步视图调用异步任务的案例里,HTTP响应在第一个异步任务执行之前已经返回。HTTP响应返回后,异步任务仍在执行直到结束。
接下来,浏览同步视图函数对应地址http://localhost:8000/sync/, 得到HTTP响应大约需要五秒钟:
Blocking HTTP request
转到终端,可以看到:
2345<Response [200 OK]>INFO: 127.0.0.1:60375 - "GET /sync/ HTTP/1.1" 200 OK
在此,HTTP响应是在同步任务完成后才返回的。
小编注:以上两个对比可以看出在Django中异步视图中调用和执行异步任务是非阻塞的,执行效率非常高。那么如果在异步视图中调用同步任务呢? 答案是与同步视图执行同步任务无区别。当你希望使用Django异步视图提升你的代码效率时,不仅视图需要是异步的,其调用的任务函数也必须是异步的。
同步转异步(sync to async)
如果您需要在异步视图内调用同步任务(比如通过Django ORM与数据库进行交互),请使用sync_to_async作为包装器或装饰器。
例子:
# hello_async/views.py async def async_with_sync_view(request): loop = asyncio.get_event_loop() async_function = sync_to_async(http_call_sync) loop.create_task(async_function()) return HttpResponse("Non-blocking HTTP request (via sync_to_async)")
使用前需要先从asgiref库导入这个方法:
from asgiref.sync import sync_to_async
使用sync_to_async
,原本阻塞HTTP响应的同步任务将会放在后台线程中处理,从而允许先返回HTTP响应再执行耗时的同步任务。
Celery与异步视图
很多人会问,Django已经有异步视图了,那么还需要Celery吗? 答案是看情况。
Django的异步视图提供了与任务或消息队列类似的功能,而且更简单。如果您正在使用(或正在考虑)Django,并且想做一些简单的事情(例如向新订阅用户发送电子邮件或调用外部API), 那么异步视图是一种快速轻松实现此目标的好方法。如果您需要执行大量,长时间运行的后台进程,则仍然需要使用Celery或RQ。
应该注意的是,为了有效地使用异步视图,您应该仅在视图中进行调用异步任务。另一方面,任务队列在单独的进程上使用工作程序,因此能够在多个服务器的后台运行同步调用。
顺便说一句,您绝对不必在异步视图和消息队列之间进行选择-您可以轻松地串联使用它们。例如:您可以使用异步视图发送电子邮件或对数据库进行一次性修改,但是Celery每晚在计划的时间清理数据库或生成并发送客户报告。
何时使用异步视图
对于未开发项目,请利用异步视图并尽可能以异步方式编写I / O流程。也就是说,如果大多数视图仅需要调用数据库并在返回数据之前进行一些基本处理,那么与传统同步视图相比,您不会看到多少效率上的提升。
对于已完成的项目,如果您几乎没有I/O进程,请坚持同步视图。如果确实有许多I/O进程,你需要衡量一下以异步方式重写它们的难度。将同步I/O重写为异步并不容易,因此您可能要在尝试重写为异步之前优化同步的I/O和视图。另外,将同步任务与异步视图混合在一起绝不是一个好主意。
在生产环境中,请务必使用Gunicorn来管理Uvicorn,以便获得高并发(通过Uvicorn)和并行性(通过Gunicorn工人)的优势。
gunicorn -w 3 -k uvicorn.workers.UvicornWorker hello_async.asgi:application
结论
尽管这是一个简单的用例,但它应该使您大致了解Django的新异步视图打开的可能性。您可以在异步视图中尝试其他一些操作,例如发送电子邮件,调用第三方API以及写入文件。考虑一下代码中具有简单过程的视图,这些视图不一定需要直接向最终用户返回任何内容,可以将这些视图快速转换为异步视图。
来源:https://testdriven.io/blog/django-async-views/