九、Django3的ASGI

九、Django3的ASGI

9.1、Web应用程序和web服务器

Web应用程序(Web)是一种能完成web业务逻辑,能让用户基于web浏览器访问的应用程序,它可以是一个实现http请求和响应功能的函数或者类,也可以是Django、Flask、sanic等这样的web框架,当然也可以是其他语言的web程序或web框架。

Web服务器(Web Server)是一种运行于网站后台(物理服务器)的软件。Web服务器主要用于提供网页浏览或文件下载服务,它可以向浏览器等Web客户端提供html网页文档,也可以提供其他类型的可展示文档,让客户端用户浏览;还可以提供数据文件下载等。目前世界上最主流的Web服务器有 Nginx 、Apache、IIS、tomcat。

问:Web服务器和Web应用程序的区别?
答:Web应用程序主要是完成web应用的业务逻辑的处理,Web服务器则主要是应对外部请求的接收、响应和转发。
    需要使用web服务器启动运行,web应用程序才能被用户访问到。
    而django框架中,我们之所以只有一个web应用程序就跑起来了,是因为我们在终端执行了一个命令,python manage.py runserver。这个命令启动了django框架中内置提供的测试web服务器。

9.2、网关接口

网关接口(Gateway Interface,GI)就是一种为了实现加载动态脚本而运行在Web服务器和Web应用程序中的通信接口,也可以理解为一份协议/规范。只有Web服务器和Web应用程序都实现了网关接口规范以后,双方的通信才能顺利完成。常见的网关接口协议:CGI,FastCGI,WSGI,ASGI。

image-20210608222947398

(1)CGI

CGI(Common Gateway Inteface): 字面意思就是通用网关接口,

它是外部应用程序与Web服务器之间的接口标准

意思就是它用来规定一个程序该如何与web服务器程序之间通信从而可以让这个程序跑在web服务器上。当然,CGI 只是一个很基本的协议,在现代常见的服务器结构中基本已经没有了它的身影,更多的则是它的扩展和更新。

FastCGI: CGI的一个扩展, 提升了性能,废除了 CGI fork-and-execute (来一个请求 fork 一个新进程处理,处理完再把进程 kill 掉)的工作方式,转而使用一种长生存期的方法,减少了进程消耗,提升了性能。

这里 FastCGI 就应用于前端 server(nginx)与后端 server(uWSGI)的通信中,制定规范等等,让前后端服务器可以顺利理解双方都在说什么(当然 uWSGI 本身并不用 FastCGI, 它有另外的协议)

(2)WSGI

WSGI(Python Web Server GateWay Interface):它是用在 python web 框架编写的应用程序与后端服务器之间的规范(本例就是 Django 和 uWSGI 之间),让你写的应用程序可以与后端服务器顺利通信。在 WSGI 出现之前你不得不专门为某个后端服务器而写特定的 API,并且无法更换后端服务器,而 WSGI 就是一种统一规范, 所有使用 WSGI 的服务器都可以运行使用 WSGI 规范的 web 框架,反之亦然。

WSGI和ASGI,都是基于Python设计的网关接口(Gateway Interface,GI)。

image-20210608223815714

Web服务器网关接口(Python Web Server Gateway Interface,WSGI),是Python为了解决Web服务器端与客户端之间的通信基于CGI标准而设计的。实现了WSGI协议的web服务器有:uWSGI、uvicorn、gunicorn。像django框架一般开发中就不会使用runserver来运行,而是采用上面实现了WSGI协议的web服务器来运行。

django中运行runserver命令时,其实内部就启动了wsgiref模块作为web服务器运行的。wsgiref是python内置的一个简单地遵循了wsgi接口规范的web服务器程序。

from wsgiref.simple_server import make_server
# application 由wsgi服务器调用、函数对http请求与响应的封装、使得Python专注与HTML
# environ http 请求 (dist)
# start_response 响应 (function)
def application(environ, start_response):
    # 请求
    if environ['REQUEST_METHOD'] == 'GET' and environ['PATH_INFO'] == '/':
        # 响应
        start_response('200 OK', [('Content-Type', 'text/html')])
        return [b'<h1>hi, python!</h1>']

if __name__ == '__main__':
    # 启动服务器 | 这个服务器负责与 wsgi 接口的 application 函数对接数据
    httpd = make_server('127.0.0.1', 8000, application)

    # 监听请求
    httpd.serve_forever()
    
    
# 1. 监听8000端口,
# 2. 把http请求根据WSGI协议将其转换到applcation中的environ参数, 然后调用application函数.
# 3. wsgiref会把application函数提供的响应头设置转换为http协议的响应头,
# 4. 把application的返回(return)作为响应体, 根据http协议,生成响应, 返回给浏览器.

开发中,我们一般使用uWSGI或者Gunicorn作为web服务器运行django。

(3)uWSGI

uWSGI 是一个快速的,自我驱动的,对开发者和系统管理员友好的应用容器服务器,用于接收前端服务器转发的动态请求并处理后发给 web 应用程序。完全由 C 编写,实现了WSGI协议,uwsgi,http等协议。注意:uwsgi 协议是一个 uWSGI服务器自有的协议,用于定义传输信息的类型,常用于uWSGI服务器与其他网络服务器的数据通信中。

uwsgi: 是uWSGI服务器实现的独有的协议, 网上没有明确的说明这个协议是用在哪里的,我个人认为它是用于前端服务器与 uwsgi 的通信规范,相当于 FastCGI的作用。

img

在这里插入图片描述

img

9.3、关于ASGI

9.3.1、历史趣谈

2019 年 12 月,Django3.0 发布,它具有一个有趣的新特性——支持 ASGI 服务器。

2003 年,诸如Zope、Quixote之类的各式各样的 Python Web 框架都附带了自己的 Web 服务器,或者拥有自己的本地接口来与流行的网络服务器(如 Apache)进行通信。

成为一名 Python Web 开发人员意味着要全身心投入到一个完整的技术栈中,但是如果需要另一个框架时,则必须重新学习所有内容。可以想象到这会导致碎片化。PEP 333,即 Python Web 服务器网关接口 v1.0,尝试通过定义一个被称为 WSGI(Web Server Gateway Interface)的简单标准接口来解决这个问题。它的卓越之处在于它的简单性。

WSGI 如此流行,以至它不仅被诸如 Django 和 Pylons 之类的大型 Web 框架所采用,还被诸如 Bottle 之类的微框架所采用。

为什么有ASGI的出现

如果我们对 WSGI 非常满意的话,为什么还要提出 ASGI 呢?如果仔细检查网络请求的整个处理流程,那么答案就非常明显了。您可以查看 在Django中网络请求如何工作 的动画。请注意,在数据库查询之后且响应发送之前,框架是如何等待的。这就是同步处理的缺点。

坦白说,直到 2009 年 Node.js 出现时,这种缺陷才变得明显或紧迫。Node.js 的创建者 Ryan Dahl 受到 C10K 问题的困扰,即为什么像 Apache 这样的流行 Web 服务器无法处理 10,000 个或更多的并发连接(给定典型的 Web 服务器硬件,内存将会耗尽)。他思考到 “查询数据库时,软件在做什么?”。

当然,答案是什么也没有发生。它正在等待数据库的响应。Ryan 认为网络服务器根本不应该去等待 I / O 活动。换言之,它应该切换为处理其他请求,并在较慢的活动完成时能得到通知。

越来越明显的是,基于异步事件的架构是解决多种并发问题的正确方法。也许这就是为什么 Python 的创建者 Guido 亲自为 Tulip 项目提供语言级别支持的原因,这个项目后来成为了 asyncio 模块。最终,Python 3.7 添加了新的关键字 async 和 await ,用于支持异步事件循环。这不仅对如何编写 Python 代码而且对代码执行也具有重大意义。

Python 的两个世界

虽然使用 Python 编写异步代码非常简单,在函数定义前添加 async 关键字,但是您必须非常小心,不要打破一个重要的规则:不要随意混合使用同步代码和异步代码。

这是因为同步代码可以阻止异步代码中的事件循环。这种情况会使您的应用程序陷入停顿。正如 Andrew Goodwin 所写:这会将您的代码分为两个世界——具有不同库和调用风格的“同步代码”和“异步代码”。

回到 WSGI,这意味着我们无法编写异步可调用对象并将其嵌入。WSGI 是为同步世界编写的。我们将需要一种新的机制来调用异步代码。但是,如果每个人都编写自己的一套机制,我们将回到最初的不兼容地狱。因此,对于异步代码,我们需要一个类似于 WSGI 的新标准。因此,ASGI 诞生了。

ASGI 还有其他一些目的。但是在此之前,让我们看一下两种类似的 Web 应用程序“Hello World”,它们分别采用 WSGI 和 ASGI 风格。

与 WSGI 一样,ASGI 可调用对象可以一个接一个地链接在一起,以处理 Web 请求(以及其他协议请求),即链式调用。实际上,ASGI 是 WSGI 的超集且可以调用 WSGI 可调用对象。ASGI 还支持长时间轮询,慢速流媒体和其他响应类型,而无需侧向加载,从而可以加快响应速度。

因此,ASGI 引入了构建异步 Web 界面和处理双向协议的新方式。客户端或服务器端都无需等待对方进行通信——这可以随时异步发生。现存且以同步代码编写的基于 WSGI 的 Web 框架将不支持这种事件驱动的工作方式。

Django 演变

同时,想将所有这些异步优势带给 Django 面临一个重大的问题 —— 所有 Django 代码都是以同步风格编写的。如果我们需要编写任何异步代码,那么需要一个采用异步风格编写的整个 Django 框架的副本。换句话说,创建两个 Django 世界。

好吧,不要惊慌——我们可能不必编写一个完整的副本,因为有聪明的方式可以在两个世界之间重用一些代码。正如领导 Django Async 项目的安德鲁·戈德温(Andrew Godwin)正确地指出的那样,“这是 Django 历史上最大规模的修订之一”。一个雄心勃勃的项目采用异步风格重新实现 ORM、请求处理程序(request handler)、模板渲染器(Template renderer)等组件。这将分阶段进行,并在多个版本中完成。如下是安德鲁的预想(这不视为已提交的时间表):

  • Django 3.0-ASGI 服务器
  • Django 3.1-异步视图
  • Django 3.2 / 4.0-异步 ORM

您可能正在考虑其余的组件,例如模板渲染、表单和缓存等。它们可能仍然保持同步或者其异步实现会纳入到将来的技术路线中。但是以上是 Django 在异步世界中发展的关键里程碑。

9.3.2、asgi的使用

ASGI,是构建于WSGI接口规范之上的异步服务器网关接口,是WSGI的延伸和扩展。

A指的是Async,异步的意思。
协议,规范 支持的请求协议(常见,未列全) 同步/异步 支持的框架
CGI HTTP   CGI程序
WSGI HTTP 同步 django3.0以前,Flask1.0
ASGI HTTP,HTTP2,WebSocket等 同步/异步 FastAPI,Quart,Sanic,Tornado,django3.0以后,flask2.0

在 Python3.5 之后增加 async/await 特性之后简化了协程操作以后,异步编程变得异常火爆,越来越多开发者投入异步的怀抱。

3.0版本以前,django所提供的所有内部功能都是基于同步编程的。所以,在以往django开发中,针对网络请求,数据库读取等IO操作形成的阻塞,往往会导致项目运行性能的下降。虽然等待I/O操作数微秒时,但是随着流量的增加和操作的频率上升,这一点点的阻塞就会导致整个项目运作的缓慢。而如果换成异步就不会有任何阻塞,还可以同时处理其他任务,从而以较低的延迟处理更多的请求。所以在目前python开发中,越来越多的框架开始支持了异步编程。所以,3.0版本以后,django开始支持异步编程,可以让开发者在django中使用python第三方异步模块,推出了asgi异步web服务器。3.1版本推出了异步视图,当然,目前django的异步编程还不够完善,django中只有极少的功能是支持了异步操作。

image-20210608235013829

Uvicorn 是一个快速的 ASGI 服务器,Uvicorn 是基于 uvloop 和 httptools 构建的,是 Python 异步生态中重要的一员。

Uvicorn 当前支持 HTTP / 1.1 和 WebSockets,将来计划支持HTTP / 2。

文档:https://www.uvicorn.org/

安装uvicorn

pip install uvicorn

项目根目录下,运行django项目

# uvicorn 主应用目录名.asgi:application --reload
uvicorn homework.asgi:application --reload

开发中一般使用gunicorn来管理uvicorn。所以可以一并安装

pip install gunicorn

运行

# gunicorn -w 4 主应用目录名.asgi:application -k uvicorn.workers.UvicornWorker --reload
gunicorn -w 4 homework.asgi:application -k uvicorn.workers.UvicornWorker --reload

 

posted @ 2022-07-01 22:46  xiaohaoge  阅读(809)  评论(0编辑  收藏  举报