ASGI简介:构建异步Python Web生态系统

哈Ha! 我向您介绍 Florimond Manca撰写的文章“ ASGI简介:异步Python Web生态系统的出现”的翻译。



“乌龟在池塘附近”,里卡德·巴拉汉姆(Ricard Baraham)在unsplash.com上


Python不仅限于数据科学,Python Web开发还伴随着语言开发方面的新的异步转变!


Python Web开发生态系统中发生了许多重要事件。 这些变化的主要驱动力之一是ASGI-异步标准网关接口。


我已经在博客中多次提到ASGI,特别是当我宣布Bocadillo异步开放源代码Python Web框架-大约 每秒钟 )和tartiflette-starlette用于通过ASGI通过HTTP构建GraphQL API的库-大约200。 ),但我从未写过有关他的详细介绍。 现在我要做。


本文针对的是对Python Web开发的最新趋势感兴趣的人们。 我想邀请您参加一次巡回演出,从中您将了解ASGI是什么,以及它对于Python世界中现代Web开发的意义。


在开始之前,我想告诉您,我最近创建了awesome-asgi ,这是跟踪不断扩展的ASGI生态系统的好清单。


一切始于异步/等待


与JavaScript或Go不同,Python在出现时并未提供异步执行代码的功能。 长期以来,只有借助多线程或多处理器处理或使用诸如eventlet,gevent或Twisted之类的专用网络库,才能在Python中实现并行代码执行。 (早在2008年,Twisted就有一个用于异步协程的API,例如inlineCallbacksdeferredGenerator的形式)


Python 3.4+中的一切都发生了变化。 在Python 3.4中, asyncio包含在标准库中,从而支持基于生成器的协同多任务处理以及语法的产生。


稍后在Python 3.5中,添加了 async/await 语法 。 因此,独立于底层实现的本地协程出现了,这导致了围绕Python并发的淘金热。


疯狂的比赛已经开始! 从3.5版发布以来,社区实际上已经同步了周围的所有内容。 如果您有兴趣,可以在aio-libsawesome-asyncio中列出许多产生的项目。


您可能会猜到,这也意味着Web服务器和Python应用程序正在朝着异步发展。 实际上,所有酷家伙都这样做! ( 甚至是Django )( Habr: Django 3.0将是异步的 ,已经在2019年2月2日发行-大约每人。


ASGI评论


那么ASGI如何适合所有这些呢?


顶级ASGI可被视为允许异步Python服务器和应用程序相互交互的链接。 他重复了WSGI的许多体系结构思想,并且经常作为内置异步的继任者出现。


这就是它在图表上的表示方式:



在很高的层次上,ASGI是应用程序和服务器之间通信的接口。 但实际上,一切都有些复杂。


要了解ASGI的实际工作原理,让我们看一下ASGI规范


ASGI包含两个不同的组件:


  1. 协议服务器—侦听套接字,并将其转换为连接和每个连接中的事件消息。
  2. 驻留在协议服务器内部的一个应用程序( application ),将为每个连接创建一次其实例,并在事件消息发生时对其进行处理。

因此,根据规范,ASGI真正指示的是消息格式以及应如何在应用程序和运行它的协议服务器之间传输这些消息。


现在我们可以制作该图的更详细的版本:



协议中还有许多有趣的细节。 例如,您可以看一下HTTP和WebSocket规范


另外,尽管该规范着重于服务器与应用程序之间的交互,但ASGI设法涵盖了更多方面。


我们在一分钟内就可以做到这一点,但首先...


ASGI基础


现在,我们已经了解了ASGI如何适合Python网络生态系统,让我们仔细研究一下它如何转换为代码。


ASGI依赖于一个简单的模型:当客户端连接到服务器时,将创建一个应用程序实例。 然后,将传入的数据传输到应用程序,并将它返回的所有数据发送回去。


在这里将数据传递到应用程序实际上意味着像调用函数一样调用该应用程序 ,即 需要一些输入并返回输出的东西。


实际上,ASGI应用程序代表的所有内容都是可调用的 (称为对象)。 同样,这个被调用对象的参数由ASGI规范确定


 async def app(scope, receive, send): ... 

该功能的签名正是“ I”在“ ASGI”中的含义:应用程序必须实现的接口,服务器才能对其进行调用。


让我们看一下函数的参数:


  • scope是一个字典,其中包含有关传入请求的信息。 对于HTTPWebSocket连接,其内容是不同的。
  • receive是一个异步函数,用于接收有关ASGI事件的消息。
  • send是用于发送有关ASGI事件的消息的异步函数。

实际上,这些参数使您可以在协议服务器支持的通信通道上接收( receive() )和发送( send() )数据,以及了解在哪个上下文(或scope )中创建此通道。


我不了解您,但我真的很喜欢此界面的总体外观和结构。 无论如何,现在让我们看一个示例代码。


显示代码!


为了了解ASGI的外观,我在裸露的ASGI上创建了一个最小的项目,该项目演示了uvicorn (流行的ASGI服务器)提供的HTTP应用程序:


 async def app(scope, receive, send): assert scope["type"] == "http" 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!", }) 

源代码-https://glitch.com/edit/#!/asgi - hello - world


在这里,我们使用send()向客户端发送HTTP响应:首先发送标头,然后发送响应主体。


我承认由于所有这些字典和原始二进制数据,裸露的ASGI并不是很方便工作。


幸运的是,这里有更高级别的选项-那就是我开始谈论Starlette的时候


Starlette是一个真正出色的项目,我认为,这是ASGI生态系统的基本组成部分。


简要地说,它提供了一组高级组件,例如请求和答案,可用于从ASGI的某些细节中抽象出来。 在这里,看看Starlette中的“ hello world”:


 # app.py from starlette.responses import PlainTextResponse async def app(scope, receive, send): assert scope["type"] == "http" response = PlainTextResponse("Hello, world!") await response(scope, receive, send) 

Starlette具有您期望从真正的Web框架中获得的一切-路由,中间件等。 但是我决定展示此精简版,以暗示ASGI的真正力量,即...


一直乌龟


ASGI的一个有趣且规则更改的概念是Turtles All the Way ,这是最初由Andrew Godwin发明(我认为是)的表达式,他创建了Django Migrations,目前正在Django上支持异步


但这到底是什么意思?


由于ASGI是一种抽象,它使我们可以说出我们所处的上下文以及随时接收和发送数据,因此有一种想法,即ASGI不仅可以在服务器和应用程序之间使用,而且可以在堆栈中的任何位置使用。


例如,Starlette Response对象是ASGI应用程序本身。 实际上,我们可以将上面示例应用程序中的代码缩短为:


 # app.py app = PlainTextResponse("Hello, world!") 

这看起来有多荒谬?


但是,等等,还不是全部。


“一路折腾”的更深层后果是,我们可以创建各种应用程序,中间件,库和其他项目,并确保它们兼容,只要它们都实现ASGI应用程序接口即可。


(此外,根据我在构建Bocadillo方面的个人经验,经常(如果不是总是)接受ASGI接口会导致代码更简洁


例如,我们可以创建一个ASGI中间件(即包装另一个应用程序的应用程序)来显示完成请求所花费的时间:


 # app.py import time class TimingMiddleware: def __init__(self, app): self.app = app async def __call__(self, scope, receive, send): start_time = time.time() await self.app(scope, receive, send) end_time = time.time() print(f"Took {end_time - start_time:.2f} seconds") 

要使用它,我们只需用它包装应用程序即可。


 # app.py import asyncio from starlette.responses import PlainTextResponse async def app(scope, receive, send): await asyncio.sleep(1) response = PlainTextResponse("Hello, world!") await response(scope, receive, send) app = TimingMiddleware(app) 

...它将神奇地起作用。


 $ uvicorn app:app INFO: Started server process [59405] INFO: Waiting for application startup. INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ... INFO: ('127.0.0.1', 62718) - "GET / HTTP/1.1" 200 Took 1.00 seconds 

TimingMiddleware是,您可以在TimingMiddleware中包装任何ASGI应用程序。 此示例中的内部应用程序非常简单,但是它可以是一个完整的,真实的项目(想象成百上千个API和WebSocket端点)-只要接口与ASGI兼容都没有关系。


(此中间件的一个版本更适合工业使用: timing-asgi 。)


为什么要打扰?


尽管我认为兼容性是一个非常有力的论据,但是使用基于ASGI的组件来构建Python Web应用程序还有许多优点。


  • 速度:ASGI应用程序和服务器的异步特性使它们变得非常快 (至少对于Python而言)-我们正在谈论每秒60k-70k的请求(假设Flask和Django在类似情况下只能达到10-20k)。
  • 功能:ASGI服务器和平台使您能够访问本质上不能使用同步代码和WSGI实现的并行功能(WebSocket,服务器发送的事件,HTTP / 2)。
  • 稳定性:ASGI作为一种规范已经存在了3年,并且3.0版被认为非常稳定。 结果,生态系统的主要部分正在稳定。

从库和工具的角度来看,我认为我们不能说已经达到了要求的水平。 但是,由于有一个非常活跃的社区,我非常希望ASGI生态系统能够很快达到与传统的同步/ WSGI生态系统相同的功能。


在哪里可以找到与ASGI兼容的组件?


实际上,越来越多的人正在基于ASGI构建和改进项目。 显然,这些是服务器和Web框架,但是也有中间件和面向产品的应用程序,例如Datasette


以下是一些我不感兴趣的非基于Web的组件的示例:



令人惊奇的是,生态系统正在成功发展,但是,对于我个人而言,要跟上变化的步伐是非常困难的。


这就是为什么我创建了awesome-asgi的原因 。 我希望这可以帮助每个人紧跟ASGI世界中发生的所有令人惊奇的事情。 (并且看到他在几天之内几乎达到了100星,我感到确实有必要在一个地方收集有关ASGI资源的信息。)


结论


尽管这看起来像实现细节,但是我确信ASGI为Python Web开发的新时代奠定了基础。


如果您想了解有关ASGI的更多信息,请查看awesome-asgi列出的各种出版物 (文章和演讲)。 如果要触摸它,请尝试以下任何项目:



这些项目是由Encode(主要是Tom Christie)创建并受其支持的。 关于创建Encode支持团队的公开讨论,因此,如果您正在寻找机会参与开源开发,那么您就有这样的机会!


在ASGI世界旅行愉快!

Source: https://habr.com/ru/post/zh-CN482936/


All Articles