Python的前9个异步Web框架
异步编程现在在Python中是一等公民。如果你是一个Web开发者,有很多令人惊叹的frameworks你可以选择!
截至目前,异步在Python社区中不再只是一个流行词。随着Python在3.5版本中发布了asyncio库,Python承认了Node.js对Web开发的影响,并引入了两个新的关键字——async
和await
。
这是一个很重要的事情,因为Python语言非常谨慎地扩展核心语法,除非有迫切的需求,这只能说明Python开发者认为异步能力有多么重要。
结果,异步编程的大门被打开了:新旧库开始使用协程特性,异步框架在人气上爆发,而且今天仍在编写新的框架。
与Node.js相当甚至更好的性能并不罕见,除非你的加载模式涉及大量的CPU密集型任务,否则没有理由为什么你不能每秒发送几千个请求。
但是足够的动力!
让我们调查目前的Python领域,并了解一些顶级的异步框架。
Tornado
出人意料的是,Tornado根本不是一个新的框架。它最初发布于2009年,从那时起,它的重点一直是提供高并发的可靠异步编程。
Tornado本质上不是一个Web框架。它是一组异步模块,也用于构建Web框架模块。更具体地说,这些模块包括:
- 协程和其他基元(
tornado.gen
,tornado.locks
,tornado.queues
等) - 网络模块(
tornado.ioloop
,tornado.iostream
等) - 异步服务器和客户端(
tornado.httpserver
,tornado.httpclient
等)
这些模块已经合并以产生最终的框架模块:tornado.web
,tornado.routing
,tornado.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并在终端中检查输出。您将看到一个包含Geekflare
和Chadan
的列表,因为我们传递了两个同名的查询参数。
如果您希望有多个不同的查询参数,也可以这样做。只需向函数添加另一个参数,它与查询参数的名称相同,并根据需要进行操作。
请求对象
我们基础知识中剩下的唯一一件事是检查其他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