异步视图

阅读: 25119     评论:4

从3.0开始,Django逐步开始了对异步视图的支持。

异步视图

Web服务器可分为同步WSGI和异步ASGI两种模式,在不同的模式下,同步和异步视图有性能差别:

  • WSGI+同步视图:传统模式
  • ASGI+异步视图:性能最佳的异步模式
  • WSGI+同步视图+异步视图:传统模式混杂异步,异步视图的性能不能正常发挥
  • ASGI+同步视图+异步视图:异步模式混杂传统视图,异步性能佳,但有拖后退的同步视图

(附ASGI官方文档地址:https://asgi.readthedocs.io/en/latest/extensions.html)

在Django中,通过async defawait语法,将函数视图定义为异步视图。

对于类视图,则是将它的__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()

需要说明的是:

  • 所有导致SQL提交的queryset查询方法都有异步版本
  • 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_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_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版本开始,同时支持同步和异步模式:

  • cache_control()
  • never_cache()
  • no_append_slash()
  • csrf_exempt()
  • csrf_protect()
  • ensure_csrf_cookie()
  • requires_csrf_token()
  • sensitive_variables()
  • sensitive_post_parameters()
  • gzip_page()
  • condition()
  • conditional_page()
  • etag()
  • last_modified()
  • require_http_methods()
  • require_GET()
  • require_POST()
  • require_safe()
  • vary_on_cookie()
  • vary_on_headers()
  • xframe_options_deny()
  • xframe_options_sameorigin()
  • xframe_options_exempt()
from django.views.decorators.cache import never_cache


@never_cache
def my_sync_view(request):
    ...


@never_cache
async def my_async_view(request):
    ...

ASGI服务器

如同传统 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
  • Hypercorn
  • Uvicorn

Daphne

Daphne是Django软件基金会开发的一个基于ASGI (HTTP/WebSocket)的服务器。

官网:https://pypi.org/project/daphne/ ,有详细的配置和启动Daphne的说明文档。

  1. 通过pip安装: python -m pip install daphne
  2. 执行daphne your_project.asgi:application命令,启动ASGI服务器
  3. 浏览器访问127.0.0.1:8000

注意要在与 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中注册daphne
  • 设置ASGI_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

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

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

使用 Uvicorn 和 Gunicorn 部署 Django

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编写异步代码,可以总结为下面四个方面:

  1. 对需要编写异步代码的视图函数使用async关键字进行定义
  2. 对异步代码使用await关键字
  3. 对异步查询操作使用以a开头的异步API
  4. 启动ASGI服务器替代传统的WSGI服务器

 视图函数及快捷方式 HttpRequest对象 

评论总数: 4


点击登录后方可评论

是否意味着部署项目优先使用asgi服务器呢,即使都是同步视图,asgi也不差于wsgi?



异步写的很清晰明了。什么时候可以出一个专门写REST API 的专栏哦。



这玩意儿看的我一脸懵,异步视图是干嘛用的?用来做什么的?



对于大多数人,异步视图不是必须的,可以忽略。