从3.0开始,Django逐步开始了对异步视图的支持。
Web服务器可分为同步WSGI和异步ASGI两种模式,在不同的模式下,同步和异步视图有性能差别:
(附ASGI官方文档地址:https://asgi.readthedocs.io/en/latest/extensions.html)
在Django中,通过async def
和await
语法,将函数视图定义为异步视图。
对于类视图,则是将它的__call__()
方法定义为async def
,成为异步类视图。
下面是一个异步视图的例子:
import datetime from django.http import HttpResponse async def current_datetime(request): now = datetime.datetime.now() html = '<html><body>It is now %s.</body></html>' % now return HttpResponse(html)
或者如下面的例子:
async def my_view(request): await asyncio.sleep(0.5) # 异步语句 return HttpResponse('Welcome to visit https//www.liujiangblog.com for Django course')
如果你不小心在异步视图中调用阻塞操作,会触发Django的异步安全保护模式(asynchronous safety protection)来保护你的数据不被破坏。
Django支持异步查询和迭代:
async for author in Author.objects.filter(name__startswith="A"): book = await author.books.afirst()
需要说明的是:
async for
语法支持所有的queryset(包括values和valuse_list)Django还支持一些模型的异步方法:
async def make_book(*args, **kwargs): book = Book(...) await book.asave(using="secondary") async def make_book_with_tags(tags, *args, **kwargs): book = await Book.objects.acreate(...) await book.tags.aset(tags)
不幸的是,事务还不能在异步模式下工作。如果你有一段代码需要事务行为,建议将该段代码编写为同步函数,并使用sync_to_async()
调用它。
为了自适应同步和异步调用的调用,Django在asgiref.sync
模块中提供了两个非常重要和关键的方法:
async_to_sync()
sync_to_async()
asgiref
库是Django生态的一部分,由Django官方组织开发,在pip安装Django的同时会自动作为依赖安装。
async_to_sync
(async_function, force_new_loop=False)异步转同步。
接收一个异步函数,并将它包裹起来,返回一个同步函数。
可以作为一个包装器或者装饰器使用。
from asgiref.sync import async_to_sync async def get_data(): ... sync_get_data = async_to_sync(get_data) # 或者这样使用 @async_to_sync async def get_other_data(): ...
async_to_sync()
本质上是 Python 标准库中 asyncio.run()
函数更强大的版本。
sync_to_async
(sync_function, thread_sensitive=False)同步转异步。
使用同步函数并返回它包装的异步函数。可用作直接包装器或装饰器:
from asgiref.sync import sync_to_async async_function = sync_to_async(sync_function, thread_sensitive=False) async_function = sync_to_async(sensitive_sync_function, thread_sensitive=True) #或者这样使用 @sync_to_async def sync_function(): ...
sync_to_async()
有两种线程模式:
thread_sensitive=False
(默认):同步函数将在一个全新的线程中运行,该线程一旦完成,将会关闭。thread_sensitive=True
: 同步函数将与所有其它 thread_sensitive=True的 函数在相同线程里运行,这个线程通常就是主线程。Thread-sensitive(线程敏感)模式非常特殊,在同一个线程中运行所有函数需要做很多工作。
下面内置的视图装饰器,从5.0版本开始,同时支持同步和异步模式:
from django.views.decorators.cache import never_cache @never_cache def my_sync_view(request): ... @never_cache async def my_async_view(request): ...
如同传统 WSGI服务器 一样,Django 需要部署 ASGI服务器来提供asgi服务。
在使用startproject
命令创建Django工程后,会自动生成一个ASGI应用配置文件,它就是<project_name>/asgi.py
。其源代码很简单:
""" ASGI config for Django5_test project. It exposes the ASGI callable as a module-level variable named ``application``. For more information on this file, see https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ """ import os from django.core.asgi import get_asgi_application os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Django5_test.settings') application = get_asgi_application()
ASGI服务器通常会以字符串形式获取asgi.py
的路径。对于大多数Django项目来说,类似your_project.asgi:application
。
如果你不想使用默认的配置文件settings.py
,也可以自己编写,然后将DJANGO_SETTINGS_MODULE
指向它。
不可以用python manage.py runserver
的方式启动ASGI服务器,这只会启动传统的、默认的WSGI服务器,也就是同步架构的服务器。我们需要安装专门的ASGI服务器。
为了应用ASGI中间件,或者将Django嵌入到其他的ASGI应用程序中,你可以将Django的 application
应用程序对象包装到 asgi.py
文件中。举个例子:
from some_asgi_library import AmazingMiddleware application = AmazingMiddleware(application)
类似WSGI下的uwsgi、gunicorn,ASGI下有三种可以使用的ASGI服务器:
Daphne是Django软件基金会开发的一个基于ASGI (HTTP/WebSocket)的服务器。
官网:https://pypi.org/project/daphne/ ,有详细的配置和启动Daphne的说明文档。
daphne your_project.asgi:application
命令,启动ASGI服务器注意要在与 manage.py
文件相同的路径中运行命令。
更多启动命令例子如下:
daphne -b 0.0.0.0 -p 8001 django_project.asgi:application daphne -u /tmp/daphne.sock django_project.asgi:application daphne --fd 5 django_project.asgi:application daphne -e ssl:443:privateKey=key.pem:certKey=crt.pem django_project.asgi:application
如果你偏爱使用python manage.py runserver
的方式启动ASGI服务器,可以将daphne集成到runserver命令中,只需要在settings.py
中如下配置:
INSTALLED_APPS
中注册daphneASGI_APPLICATION
INSTALLED_APPS = [ "daphne", ..., ] ASGI_APPLICATION = "myproject.asgi.application"
更多关于ASGI服务器的内容,请阅读博客https://www.liujiangblog.com/blog/47/
如果在安装Twisted的过程中发生错误,请前往https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted下载对应Python版本的wheel文件,然后pip install Twisted....wheel
可能需要提前pip install wheel。
Hypercorn 是一个加强支持HTTP/1,HTTP/2和HTTP/3的ASGI服务器。
安装方法:
python -m pip install hypercorn
对于典型的 Django 项目,调用 Hypercorn 的方式如下:
hypercorn myproject.asgi:application
它将开启一个进程,监听 127.0.0.1:8000
。这需要你的项目位于 Python path 上,为了确保这点,你应该在与 manage.py
文件相同的路径中运行这个命令。
Uvicorn 是一个基于 uvloop 和 httptools 的加强运行速度的ASGI服务器。
安装方法:python -m pip install uvicorn
对于典型的 Django 项目,调用 Uvicorn 的方式如下:
python -m uvicorn myproject.asgi:application
它将开启一个进程,监听 127.0.0.1:8000。这需要你的项目位于 Python path 上,为了确保这点,你应该在与 manage.py
文件相同的路径中运行这个命令。
在开发模式下,可以添加--reload
参数,开启自动重载功能。
有关更多的高级用法,请参阅官网Uvicorn
Gunicorn 是一个强大的 Web 服务器,实现了进程监控和自动重启,在生产环境中可以搭配 Uvicorn 使用。
安装 Uvicorn 和 Gunicorn,使用以下命令:
python -m pip install uvicorn gunicorn
然后像下面这样使用 Uvicorn worker 类启动 Gunicorn :
python -m gunicorn myproject.asgi:application -k uvicorn.workers.UvicornWorker
使用Django编写异步代码,可以总结为下面四个方面:
a
开头的异步API
是否意味着部署项目优先使用asgi服务器呢,即使都是同步视图,asgi也不差于wsgi?
异步写的很清晰明了。什么时候可以出一个专门写REST API 的专栏哦。
这玩意儿看的我一脸懵,异步视图是干嘛用的?用来做什么的?
对于大多数人,异步视图不是必须的,可以忽略。