Python的前9个异步Web框架

异步编程现在在Python中是一等公民。如果你是一个Web开发者,有很多令人惊叹的frameworks你可以选择!

截至目前,异步在Python社区中不再只是一个流行词。随着Python在3.5版本中发布了asyncio库,Python承认了Node.js对Web开发的影响,并引入了两个新的关键字——asyncawait

这是一个很重要的事情,因为Python语言非常谨慎地扩展核心语法,除非有迫切的需求,这只能说明Python开发者认为异步能力有多么重要。

结果,异步编程的大门被打开了:新旧库开始使用协程特性,异步框架在人气上爆发,而且今天仍在编写新的框架。

与Node.js相当甚至更好的性能并不罕见,除非你的加载模式涉及大量的CPU密集型任务,否则没有理由为什么你不能每秒发送几千个请求。

但是足够的动力!

让我们调查目前的Python领域,并了解一些顶级的异步框架。

Tornado

出人意料的是,Tornado根本不是一个新的框架。它最初发布于2009年,从那时起,它的重点一直是提供高并发的可靠异步编程。

Tornado本质上不是一个Web框架。它是一组异步模块,也用于构建Web框架模块。更具体地说,这些模块包括:

  • 协程和其他基元(tornado.gentornado.lockstornado.queues等)
  • 网络模块(tornado.iolooptornado.iostream等)
  • 异步服务器和客户端(tornado.httpservertornado.httpclient等)

这些模块已经合并以产生最终的框架模块:tornado.webtornado.routingtornado.template等。

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

Tornado在Python社区中拥有强大而忠诚的追随者,并被有经验的架构师用于构建高性能的系统。它是一个长期以来始终解决并发问题的框架,但由于不支持WSGI标准,并且对于大多数Python库来说仍然是同步的,所以可能没有变得主流。

Sanic

Sanic是一个“现代”的框架,它不支持Python版本低于3.6,支持简单和通用的async/await语法,因此在你编写第一个HTTP处理程序之前,你不需要阅读大量的文档并在脑海中记住边界条件。

因此,最终的语法非常愉快(至少在我看来);它类似于你用任何其他微框架(例如Flask、CherryPy)编写的代码,只是有一些async

from sanic import Sanic
from sanic.response import json

app = Sanic()

@app.route("/")
async def test(request):
    return json({"hello": "world"})

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)

Sanic被认为是Python世界中最受欢迎和最受喜爱的async框架。它几乎拥有您在项目中所需要的所有功能 – 路由、中间件、cookies、版本控制、蓝图、基于类的视图、静态文件、流式传输、套接字等等。而它没有提供的功能 – 模板、数据库支持、文件I/O、队列 – 可以通过现有的足够的异步库来添加。

Vibora

Vibora是Sanic的近亲,但它专注于成为最快的Python Web服务器。事实上,当你访问它的网站时,你会看到一个框架比较:

正如你所见,Vibora声称比经典的框架快几倍,并且比其最近的竞争对手Sanic快两倍以上。当然,要对基准测试保持怀疑态度。:-)

尽管在语法和功能上,Vibora与Sanic相当(可能甚至更好,因为它捆绑了流行的库,像模板功能可以直接使用),但是我认为Sanic更成熟,因为它在市场上存在更长时间,拥有更大的社区。

from vibora import Vibora, JsonResponse

app = Vibora()

@app.route('/')
async def home():
    return JsonResponse({'hello': 'world'})

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8000)

如果你是性能追求者,Vibora可能会吸引你。然而,就目前而言,Vibora正在进行一次完全重写,以变得更快,并且其性能版本的link表示它正在”大规模开发”中。对于那些早期采用Vibora并且很快就必须面对破坏性改变的人来说,这将是一个失望,但是嘿,Python异步世界还处于早期阶段,没有人期望事情是稳定的。

Quart

如果你喜欢在Flask中开发但又为缺乏异步支持而烦恼,你会非常喜欢Quart

Quart符合ASGI标准,这是著名的WSGI标准的继任者,并提供异步支持。Quart有趣的地方在于它不仅与Flask类似,而且实际上符合Flask API的规范!这个框架的作者希望保留Flask的特点,只是添加了异步、WebSockets和HTTP 2的支持。因此,你可以直接从Flask的文档中学习Quart,只需记住Quart中的函数是异步的。

from quart import Quart

app = Quart(__name__)

@app.route('/')
async def hello():
    return 'hello'

app.run()

感觉(几乎)就像Flask一样,不是吗?!

由于Quart是Flask的进化版本,所有Flask内部的功能都可用:路由、中间件、会话、模板、蓝图等等。事实上,你甚至可以在Quart中直接使用Flask扩展。唯一的限制是仅支持Python 3.7+,但是,如果你没有运行最新版本的Python,也许异步不是正确的选择。:-)

如果你之前没有使用Flask的经验,那么Quart的文档可能确实有所不足,但我可以推荐Quart,因为它可能是唯一一个即将发布1.0版本的异步框架。

FastAPI

这个列表中最后(但最令人印象深刻)的框架是FastAPI。不,它不仅仅是一个API框架;事实上,在我研究异步Python框架时,FastAPI似乎是功能最丰富、文档最丰富的框架。

有趣的是,这个框架的作者深入研究了其他几个框架,从当代的Django到现代的Sanic,以及跨技术领域研究了NestJS(一个Node.js、TypeScript的Web框架)。他们的开发理念和广泛的比较可以在here中阅读到。

语法非常愉快;甚至可以说它比我们遇到的其他框架更加令人愉快:

从fastapi导入FastAPI

app = FastAPI()

@app.get("/users/me")
async def read_user_me():
    return {"user_id": "当前用户"}

@app.get("/users/{user_id}")
async def read_user(user_id: str):
    return {"user_id": user_id}

现在,以下是让FastAPI胜过其他框架的强大功能列表:

自动生成API文档:一旦编写了端点,您可以使用符合标准的UI来使用API。支持SwaggerUI、ReDoc和其他工具。

该框架还可以自动对数据模型进行JSON Schema文档化。

现代化的开发:是的,“现代化”这个词经常被人们提起,但我发现FastAPI确实做到了。依赖注入和类型提示是一等公民,不仅强制执行良好的编码原则,还可以防止长期存在的错误和混乱。

广泛的文档:我不知道你怎么看,但我对好文档非常着迷。在这方面,FastAPI绝对是赢家。它有很多页的文档,几乎解释了开发者在各个级别上的每一个微妙之处和“注意!”时刻。我在这里感受到了明确的“心与魂”,唯一可以相提并论的是Django的文档(是的,FastAPI的文档就是这么好!)。

超越基本功能:FastAPI还支持WebSockets、Streaming以及GraphQL,除了拥有CORS、会话、cookie等传统辅助功能。

那性能如何呢?嗯,FastAPI是基于了令人惊叹的Starlette库构建的,因此性能与Node相当,甚至在某些情况下甚至超过Go!总之,我真的有种感觉,FastAPI将成为Python中最顶级的异步框架。

BlackSheep

可以使用BlackSheep来创建服务器端或全栈应用程序,并采用MVC模式。

BlackSheep提供的一些功能包括:

  • 丰富的代码API。
  • 内置的依赖注入。
  • 自动生成OpenAPI文档。
  • 自动绑定请求处理程序。

项目设置

让我们使用BlackSheep创建一个基本的服务器端应用程序。依次运行以下命令来设置项目。

python -m venv basic-app
cd basic-app
source bin/activate
pip install blacksheep uvicorn

在CentOS、Ubuntu和Windows上学习how to install PIP软件包安装程序。

设置项目完成。让我们创建一个名为server.py的文件,并放入以下代码。

from blacksheep import Application

app = Application()

@app.router.get("/")
def home():
    return "Hello, World!"

我们创建了最著名的Hello World应用程序。目前只有一个根路径HTTP GET方法,且为/。在BlackSheep中,函数home被称为请求处理程序。

我们使用了应用程序的router装饰器。还有另一种创建路由的方法,即route。在本教程中,我们将使用router。您可以在docs中了解更多关于route的信息。

使用以下命令运行应用程序。

uvicorn server:app --port 8000 --reload

在浏览器中打开http://localhost:8000/。您将在浏览器中看到Hello World。让我们稍微讨论一下我们用来运行应用程序的命令。

  • 我们使用uvicorn包来运行我们的应用程序。
  • server是我们给定的文件名。如果您使用不同的文件名,请在启动命令中进行更改。
  • --port选项用于指定应用程序应该在哪个端口上运行。
  • 最后,--reload选项是为了在我们对server文件进行更改时,在浏览器中重新加载应用程序。

JSON响应

在现实世界中,我们在大多数情况下需要以JSON格式返回API响应。我们可以使用blacksheep包中的json将JSON对象包装起来,从方法中返回JSON响应。让我们看看如何做到。

from blacksheep import Application, json

app = Application()

@app.router.get("/")
def home():
    return json({"message": "Hello, World!"})

我们导入了blacksheep中的json并将JSON对象包装起来。在浏览器中检查JSON响应。

路由参数

有时我们需要接受路由参数进行请求。在BlackSheep中,我们可以在HTTP方法内部定义路由参数。让我们看看如何做到。

@app.router.get("/{name}")
def home(name):
    return json({"greetings": f"Hello, {name}!"})

我们接受了一个名为name的路由参数。转到http://localhost:8000/Geekflare。该方法将返回一个带有路由中给定的名称的问候。

路由装饰器将使用相同的名称将参数传递给home函数,就像装饰器中给出的那样。在这里,它将是name。如果您在装饰器中更改了它,请在home函数中也进行更改。

我们可以以类似的方式接受尽可能多的路由参数。让我们看一个快速示例。

@app.router.get("/{name}/{info}")
def home(name, info):
    return json({"greetings": f"Hello, {name}! Info {info}"})

我们接受了另一个名为info的路由参数。转到http://localhost:8000/Geekflare/Chandan进行检查。

查询参数

我们不需要做任何操作来接受查询参数。BlackSheep会自动将查询参数以列表的形式发送到函数中。让我们看一个例子。

@app.router.get("/")
def home(name):
    print(name)
    return json({"greetings": f"Hello, {name[0]}!"})

转到http://localhost:https://yaoweibin.cn/python-asynchronous-web-frameworks/8000/?name=Geekflare检查响应。如果您有多个同名的查询参数,BlackSheep会将它们全部添加到列表中。

转到http://localhost:https://yaoweibin.cn/python-asynchronous-web-frameworks/8000/?name=Geekflare&name=Chandan并在终端中检查输出。您将看到一个包含GeekflareChadan的列表,因为我们传递了两个同名的查询参数。

如果您希望有多个不同的查询参数,也可以这样做。只需向函数添加另一个参数,它与查询参数的名称相同,并根据需要进行操作。

请求对象

我们基础知识中剩下的唯一一件事是检查其他HTTP方法。在进入之前,让我们先检查API的request对象。

BalckSheep中的所有请求处理程序都会有一个request参数,其中包含来自请求的所有信息。它包括请求头、路径参数、查询参数、数据等。

让我们看一个例子来查看请求对象。

@app.router.post("/")
def home(request):
    print(request)
    return "Hello, World!"

您可以在终端中看到以下输出。

我们可以从请求中访问不同的内容。您可以查看相关文档。这里,我们关注的是请求体。让我们看看如何从请求对象中访问请求体。

@app.router.post("/")
async def home(request):
    data = await request.json()
    print(data)
    return "Hello, World!"

请求中有一个名为json的方法,它将返回来自请求的数据。在API请求中传递一些数据并调用它。您将在终端中看到您传递给API的数据打印出来。

HTTP方法

我们在上面的示例中看到了GET和POST方法。类似地,您也可以使用PUT、DELETE等方法。由于它们非常直观,尝试自己使用不会有问题。

AIOHTTP

aiohttp是另一个具有以下关键功能的框架。

  • 它支持服务器端和客户端WebSockets。
  • 它支持服务器和客户端应用程序开发。
  • 它的Web服务器具有中间件、信号和可插拔路由。

项目设置

让我们使用aiohttp创建一个基本的服务器端应用程序。快速运行以下命令来设置项目。

python -m venv basic-app
cd basic-app
source bin/activate
pip install aiohttp aiodns

创建一个名为server.py的文件,并将以下代码放入其中。

from aiohttp import web

async def home(request):
    return web.Response(text="Hello, World!")

app = web.Application()
app.add_routes([web.get('/', home)])

web.run_app(app)

web.Application实例是我们的主要应用程序。我们已经添加了HTTP GET方法,并且路由为/,返回我们喜爱的hello world。web.run_app函数用于运行应用程序,它以web.Application实例作为参数。

函数home在aiohttp中被称为请求处理程序。它只有一个参数,即request,其中包含传入请求的所有信息。

使用以下命令运行应用程序。与运行普通Python程序相同。

python3 server.py

在浏览器中转到http://localhost:8080/。您将在浏览器中看到hello world。

JSON响应

我们可以使用web.json_response函数以JSON格式返回响应。在返回响应时将JSON数据传递给该函数。让我们看一个例子。

async def home(request):
    return web.json_response({"message": "Hello, World!"})

如果您转到http://localhost:8080/,您将在浏览器中看到JSON对象。

路由参数

我们可以在添加路由时定义路由参数,并且可以从请求处理程序的request参数中访问它们。让我们看一个例子。

from aiohttp import web

async def home(request):
    return web.json_response({"message": f"Hello, {request.match_info['name']}!"})

app = web.Application()
app.add_routes([web.get('/{name}', home)])

web.run_app(app)

所有路由参数都可以从request.match_info中访问,如上例所示。转到http://localhost:8080/Geekflare进行检查。

我们还可以使用正则表达式匹配路由。假设我们只接受/{any_number}。我们可以将'/{name}'替换为r'/{number:d+}'。我们已经将正则表达式添加到路径参数中,仅当通过正则表达式时才接受。

让我们看一个例子

from aiohttp import web

async def home(request):
    return web.json_response({"message": f"你好,{request.match_info['number']}!"})

app = web.Application()
app.add_routes([web.get(r'/{number:d+}', home)])

web.run_app(app)

打开 http://localhost:8080/Geekflare,你将会看到404错误,因为Geekflare不符合我们给定的正则表达式模式。现在,访问 http://localhost:8080/1234567890 你将在浏览器中看到响应。

查询参数

不需要添加任何内容来接受查询参数。我们可以从request.query对象中接受查询参数。让我们看一个例子。

from aiohttp import web

async def home(request):
    return web.json_response({"message": f"你好,{request.query.get('name')}"})

app = web.Application()
app.add_routes([web.get('/', home)])

web.run_app(app)

打开 http://localhost:8080/?name=Geekflare 并检查结果。你将会在响应中看到我们从request.query获取到的Geekflare

我们也可以传递多个查询参数,可以通过它们的键名访问。

HTTP方法

我们已经看到了如何创建一个HTTP GET方法的示例。在继续之前,我们需要知道如何访问请求数据。让我们看一个例子。

from aiohttp import web

async def home(request):
    data = await request.json()
    print(data)
    return web.json_response({"message": f"你好,世界!"})

app = web.Application()
app.add_routes([web.post('/', home)])

web.run_app(app)

在上面的例子中,我们将API方法从GET更改为POST。我们使用request.json方法访问请求数据。

发起一个POST请求到 http://localhost:8080/,你将在终端中看到请求数据打印出来。

类似地,你也可以使用PUT、DELETE等方法。自己尝试并享受其中乐趣。

你可以继续探索该框架的更多内容,详情请参阅他们的docs

Falcon

Falcon是一个用于构建REST API和微服务的ASGI框架。它具有以下主要特点。

  • 支持WebSockets。
  • 支持用于请求处理的中间件和钩子。
  • 简单直观的异常处理。

项目设置

让我们设置一个项目来学习falcon框架的基础知识。使用以下命令设置项目。

python -m venv basic-app
cd basic-app
source bin/activate
pip install falcon uvicorn

创建一个名为server.py的文件,将以下代码放入其中。

from falcon import asgi
import falcon


class Home:
    async def on_get(self, request, response):
        response.status = falcon.HTTP_200  # 这是默认状态
        response.content_type = falcon.MEDIA_TEXT  # 默认为JSON,所以覆盖为文本
        response.text = '你好,世界!'

app = asgi.App()
app.add_route('/', Home())

我们创建了一个包含on_get方法的类,这个方法是HTTP GET方法。该方法有两个参数。一个是request,另一个是response。根据它们的名称,你应该已经猜到它们代表什么。

request参数包含所有传入请求的信息,可以用来处理请求。而response参数用于通过设置不同的属性来发送响应。

与BlackSheep和AIOHTTP不同,我们不需要返回一个响应。我们可以使用响应并设置我们需要发送的任何细节作为响应。在上面的例子中,我们将状态设置为200,内容类型设置为文本,并将文本设置为hello world。

使用以下命令运行应用程序

uvicorn server:app --reload

访问 http://localhost:8000/,您将看到Hello World作为响应。

JSON响应

我们可以使用json.dumps方法将数据转换为JSON,并将响应返回为JSON。让我们看一个例子

from falcon import asgi
import falcon
import json

class Home:
    async def on_get(self, request, response):
        response.text = json.dumps({"greetings": "Hello, World!"})

app = asgi.App()
app.add_route('/', Home())

访问 http://localhost:8000/,您将看到以JSON格式返回的响应。

路由参数

请求参数作为参数传递给HTTP方法。您可以查看以下示例以更好地理解。

from falcon import asgi
import falcon
import json

class Home:
    async def on_get(self, request, response, name):
        response.text = json.dumps({"greetings": f"Hello, {name}!"})

app = asgi.App()
app.add_route('/{name}/', Home())

name路径参数将作为参数传递给on_get方法。访问 http://localhost:8000/Geekflare/ 并检查响应。我们可以有任意多个路由参数。

查询参数

我们可以使用request.get_param(param_name)方法访问查询参数。查看下面的示例。

from falcon import asgi
import falcon
import json

class Home:
    async def on_get(self, request, response):
        response.text = json.dumps({"greetings": f"Hello, {request.get_param('name')}!"})

app = asgi.App()
app.add_route('/', Home())

访问 http://localhost:https://yaoweibin.cn/python-asynchronous-web-frameworks/8000/?name=Geekflare 检查响应。我们可以有任意多个查询参数。

HTTP方法

我们已经在上面的示例中看到了GET方法。我们需要了解其他方法的一件事是如何访问请求数据。我们可以使用request.stream.read方法访问请求数据。让我们看一个例子。

from falcon import asgi
import falcon
import json

class Home:
    async def on_get(self, request, response):
        response.text = json.dumps({"greetings": "Hello, World!"})

    async def on_post(self, request, response):
        data = await request.stream.read()
        print(json.loads(data))
        response.text = "Hello, World!"

app = asgi.App()
app.add_route('/', Home())

我们添加了一个POST方法,其中我们访问了请求数据,并在将其转换为JSON后将其打印到终端。发送POST请求并附带一些数据来检查输出。

尝试自己添加其他HTTP方法,如DELETE,PUT等。我们只转换了falcon框架的基础知识。但是,其中还有很多。去阅读 docs 以更深入地了解。

Starlette

Starlette 是一个轻量级的Python ASGI框架。它几乎具有构建服务器端应用程序的所有基本功能。

使用以下命令设置项目。

python -m venv basic-app
cd basic-app
source bin/activate
pip install starlette uvicorn

使用Starlette创建API与我们在上一个框架中看到的类似。语法和创建API的方式有所不同。所有概念都是相同的。因此,我们将把所有内容都包含在一个单独的程序中。

创建一个名为server.py的文件,并放置以下代码。

从星运应用程序导入Starlette
从星运响应导入纯文本响应、JSON响应
从星运路由导入路由

def主页(请求):
返回纯文本响应('你好,世界!')

def json_response(请求):
返回JSON响应({‘message':'你好,世界!' })

def path_params(请求):
名称=请求.path_params [‘name'] 返回JSON响应({‘问候':f'你好,{name}!'})

def query_params(请求):
名称=请求.query_params [‘name'] 返回JSON响应({‘问候':f'你好,{name}!'})

async def post_method(请求):
数据=等待请求.json()
打印(数据)
返回JSON响应({‘message':f'你好,世界!' })

def startup():
打印('Starlette已启动')

路线= [
路由('/',homepage),
路由('/ json',json_response),
路由('/ path-params / {name}',path_params),
路由('/ query-params',query_params),
路由('/ post',post_method,methods = [‘POST']),
]

应用程序= Starlette(debug = True,routes = routes,on_startup = [startup])

使用以下命令运行应用程序。

uvicorn server:app –reload

测试我们在之前的框架中看到的所有内容。您可以在其链接_20中了解有关Starlette框架的更多信息。
结论
在Python异步景观中有很多事情要做。新的链接_0正在冒出来,旧的正在被重写,并且正在演变的库以匹配异步行为。虽然Python内置了对事件循环的支持,并且可以使应用程序的部分异步化,但您可以选择全力以赴,并在这里之一上构建。

只需确保保持长期规划:目前存在的几个Python异步框架处于早期阶段,并且正在迅速发展,这将损害您的开发过程并增加业务成本。

谨慎是关键!

但是,总的来说,Python在Web框架方面提供了出色的性能。如果长期以来您一直在考虑迁移到Node,现在您不需要了! 🙂

听起来很酷吧? 链接_22

类似文章