ASGI解释:Python Web开发的未来
译者 | 李睿
审校 | 孙淑娟
Python Web应用程序长期以来一直遵循Web服务器网关接口(WSGI)标准,该标准描述了它们如何与Web服务器通信。WSGI最初于2003年推出,并于2010年更新,仅依赖于Python2.2版本中原生可用的、易于实现的功能。因此, WSGI迅速融入了所有主要的Python Web框架,并成为Python Web开发的基石。
快进到2022年。Python2已经过时,Python现在具有处理网络调用等异步操作的原生语法。WSGI和其他默认假定同步行为的标准无法利用异步的性能和效率增益。这反过来又意味着WSGI不能有效地处理像WebSocket这样的高级协议。
输入ASGI,即异步服务器网关接口。与WSGI类似,ASGI描述了Python Web应用程序和Web服务器之间的通用接口。与WSGI不同的是,ASGI允许每个应用程序有多个异步事件。另外,ASGI支持同步和异步应用程序。开发人员可以将原有的同步WSGI Web应用程序迁移到ASGI,也可以使用ASGI构建新的异步Web应用程序。
1.WSGI的工作原理
WSGI的工作原理是向Web服务器公开Python函数,通常命名为应用程序或app。该函数采用两个参数:
- environ:包含有关当前请求和Web服务器提供的环境变量的信息的字典。
- start_response:将用于启动将HTTP响应发送回客户端的函数。
函数返回的数据构成响应体。
一个简单的应用程序函数可能如下所示:
def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) return [b'Greetings universe']
如果使用的是与WSGI兼容的Web框架(例如Flask),那么该框架本身将提供一个应用程序功能,其所有组件将自动连接。
WSGI有两个缺点:首先,WSGI一次只处理一个请求和响应,并假设响应将立即返回。没有办法处理长时间保持的连接,例如WebSocket或长轮询HTTP连接。
其次,WSGI只是同步的。即使使用多线程连接池,每个连接都会阻塞,直到它返回响应。许多WSGI设置都能够处理线程池和进程池,但这些都受到WSGI接口本身同步的限制。
2.ASGI的工作原理
ASGI在外观上与WSGI相似。与WSGI一样,开发人员可以定义一个应用程序函数对象,但它是一个带有三个参数而不是两个参数的异步函数:
scope:包含有关当前请求的信息的字典,类似于WSGI中的environ,但细节的命名约定略有不同。
send:允许应用程序将消息发送回客户端的异步可调用函数。
receive:允许应用程序从客户端接收消息的异步可调用函数。
一个简单的ASGI应用程序函数如下所示:
async def application(scope, receive, send): await send({ 'type': 'http.response.start', 'status': 200, 'headers': [ [b'content-type', b'text/plain'], ], }) await send({ 'type': 'http.response.body', 'body': b'Hello, world!', })
与WSGI Web框架一样,ASGI Web框架将生成自己的application()函数,并根据需要将其连接起来。
与ASGI最明显的区别是,在整个函数中使用了异步隐喻。该函数本身是异步的,在此通过两个单独的await send( )命令发送HTTP标头和响应正文。这样,函数本身及其发送命令就不会阻塞任何内容;它们可以与应用程序的调用交织在一起,并可以同时从许多其他连接发送。
在这个例子中没有使用receive,但它也是一个异步函数。它让在不阻塞其他操作的情况下接收请求正文。请求和响应可以以这种方式增量地传入或传出服务器——这是无法很好地完成的事情,或者可能根本无法使用WSGI完成的。
3.在ASGI中使用同步和异步函数
使用ASGI时,需要尽可能多地使用异步函数和异步友好库。养成使用异步的习惯是值得的,因为使用仅同步代码的问题可能很严重。任何对同步函数的长时间调用都会阻塞整个调用链,从而使使用异步的好处几乎消失殆尽。
如果在使用长时间运行的同步调用时遇到问题,需要使用asyncio.run_in_executor将调用外包给线程池或进程池。每当等待外部事件或非CPU密集型任务时,都应使用线程池。而进程池应该用于CPU密集型的本地任务。
例如,Web应用程序中有一个路由可以调用远程网站,那么应该使用线程。或者更好的方法是,使用发出异步HTTP请求的aiohttp库。如果想调用Pillow图像库来调整图像大小,可能应该使用带有进程池的run_in_executor。尽管在进程之间来回传输数据会有一些轻微的开销,但使用run_in_executor不会阻塞其他事件。
4.支持ASGI的Web框架
通过实现application( )对象,可以人工编写ASGI Web应用程序。但绝大多数情况下,使用异步原生、以ASGI为中心的Python Web框架会更简单。以下是一些常见的与ASGI兼容的Web框架:
Starlette和FastAPI:这些新兴框架(FastAPI构建在Starlette之上)都是异步优先的,因此它们都支持ASGI也就不足为奇了。如果是从零开始开发Web应用程序,那么它们是Python最现代和最前沿的Web框架。
Quart:虽然主要的Python Web框架Flask确实支持ASGI,但Flask的设计并不是从内到外利用异步隐喻。来自GitLab的Quart使用Flask的语法和隐喻,但允许异步路由处理程序。
Django 3.0及更高版本:从Django3.0开始,久负盛名的Django Web框架支持ASGI。在Django 3.1中添加了对Django应用程序中异步代码的支持,而不是仅仅能够在ASGI处理程序上挂载Django。对于一个不以执行速度著称的框架来说,异步的存在为那些选择利用它的人带来了更好的性能。
原文链接:https://www.infoworld.com/article/3658336/asgi-explained-the-future-of-python-Web-development.html