原文转载自「刘悦的技术博客」https://v3u.cn/a_id_167
兴许这一篇的题目有那么一点不厚道,因为 Asgi(Asynchronous Server Gateway Interface)毕竟是 Wsgi(Web Server Gateway Interface)的扩大,而 FastAPI 毕竟也是站在 Flask 的肩膀上才有了突飞猛进的倒退,大多数人据说 Asgi 兴许是因为 Django 的最新版 (3.0) 早已发表反对 Asgi 网络标准,这显然是一个振奋人心的音讯,2020 年,如果你在 Web 开发面试中不扯一点 Asgi,显然就有点落后于局势了。
那么到底啥是 Wsgi,什么又是 Asgi,释怀,不扯 CGI,不扯各种抽象概念,简略粗犷了解:
Wsgi 是同步通信服务标准,客户端申请一项服务,并期待服务实现,只有当它收到服务的后果时,它才会持续工作。当然了,能够定义一个超时工夫,如果服务在规定的工夫内没有实现,则认为调用失败,调用方持续工作。
Wsgi 简略工作原理示意图:
简略实现:
#WSGI example
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return b'Hello, Wsgi\n'
Asgi 是异步通信服务标准。客户端发动服务呼叫,但不期待后果。调用方立刻持续其工作,并不关怀后果。如果调用方对后果感兴趣,有一些机制能够让其随时被回调办法返回后果。
Asgi 简略工作原理示意图:
简略实现:
#Asgi example
async def application(scope, receive, send):
event = await receive()
...
await send({"type": "websocket.send", ...})
简略总结一下:Asgi 是异步的,Wsgi 是同步的,而基于 Wsgi 的 Flask 是同步框架,基于 Asgi 的 FastAPI 是异步框架,就这么简略,那么同步框架和异步框架的区别到底在哪儿?为什么要把 Flask 换成 FastAPI?
不靠拍脑门儿、也不是一人传虚; 万人传实、随声附和。玩技术的应该用数据谈话,论点永远依靠论据,所以咱们来简略对两款框架的性能做一个测试,首先别离装置依赖的库。
Flask:
pip install gunicorn
pip install gevent
pip install flask
FastAPI:
pip install fastapi
pip install uvicorn
咱们首先干的一件事就是,看看 Flask 和 FastAPI 如何解决来自多个客户端的多个申请。特地是当代码存在效率问题时(比方数据库查问工夫长这种耗时工作),这里成心应用 time.sleep()来模仿耗时工作,为什么不必 asyncio 呢?因为家喻户晓的起因:time.sleep 是阻塞的。
Flask:
from flask import Flask
from flask_restful import Resource, Api
from time import sleep
app = Flask(__name__)
api = Api(app)
class Root(Resource):
def get(self):
print('睡 10 秒')
sleep(10)
print('醒了')
return {'message': 'hello'}
api.add_resource(Root, '/')
if __name__ == "__main__":
app.run()
FastApi:
import uvicorn
from fastapi import FastAPI
from time import sleep
app = FastAPI()
@app.get('/')
async def root():
print('睡 10 秒')
sleep(10)
print('醒了')
return {'message': 'hello'}
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)
别离启动服务
Flask:python3 manage.py
FastAPI:uvicorn manage:app –reload
同时一时间内,开启多个浏览器,别离并发申请首页。
Flask:http://localhost:5000
FastAPI:http://localhost:8000
察看后盾打印后果:
Flask:
FastAPI:
能够看到,同样的四次申请,Flask 先是阻塞了 40 秒,而后顺次返回后果,FastAPI 则是第一次阻塞后间接返回,这代表了在 FastAPI 中阻塞了一个事件队列,证实 FastAPI 是异步框架,而在 Flask 中,申请可能是在新线程中运行的。将所有 CPU 绑定的工作移到独自的过程中,所以在 FastAPI 的例子中,只是在事件循环中 sleep(所以异步框架这里最好不要应用 time.sleep 而是 asyncio.sleep)。在 FastAPI 中,异步运行 IO 绑定的工作。
当然这不能阐明太多问题,咱们持续应用鼎鼎有名的 ApacheBench 别离对两款框架进行压测。
一共设置 5000 个申请,QPS 是 100(请原谅我的机器比拟渣)。
ab -n 5000 -c 100 http://127.0.0.1:5000/
ab -n 5000 -c 100 http://127.0.0.1:8000/
这里为了偏心起见,Flask 配合 Gunicorn 服务器,开 3 个 worker,FastAPI 配合 Uvicorn 服务器,同样开 3 个 worker。
Flask 压测后果:
liuyue:mytornado liuyue$ ab -n 5000 -c 100 http://127.0.0.1:5000/
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient)
Completed 500 requests
Completed 1000 requests
Completed 1500 requests
Completed 2000 requests
Completed 2500 requests
Completed 3000 requests
Completed 3500 requests
Completed 4000 requests
Completed 4500 requests
Completed 5000 requests
Finished 5000 requests
Server Software: gunicorn/20.0.4
Server Hostname: 127.0.0.1
Server Port: 5000
Document Path: /
Document Length: 28 bytes
Concurrency Level: 100
Time taken for tests: 4.681 seconds
Complete requests: 5000
Failed requests: 0
Total transferred: 1060000 bytes
HTML transferred: 140000 bytes
Requests per second: 1068.04 [#/sec] (mean)
Time per request: 93.629 [ms] (mean)
Time per request: 0.936 [ms] (mean, across all concurrent requests)
Transfer rate: 221.12 [Kbytes/sec] received
FastAPI 压测后果:
liuyue:mytornado liuyue$ ab -n 5000 -c 100 http://127.0.0.1:8000/
This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient)
Completed 500 requests
Completed 1000 requests
Completed 1500 requests
Completed 2000 requests
Completed 2500 requests
Completed 3000 requests
Completed 3500 requests
Completed 4000 requests
Completed 4500 requests
Completed 5000 requests
Finished 5000 requests
Server Software: uvicorn
Server Hostname: 127.0.0.1
Server Port: 8000
Document Path: /
Document Length: 19 bytes
Concurrency Level: 100
Time taken for tests: 2.060 seconds
Complete requests: 5000
Failed requests: 0
Total transferred: 720000 bytes
HTML transferred: 95000 bytes
Requests per second: 2426.78 [#/sec] (mean)
Time per request: 41.207 [ms] (mean)
Time per request: 0.412 [ms] (mean, across all concurrent requests)
Transfer rate: 341.27 [Kbytes/sec] received
不言而喻,5000 个总申请,Flask 破费 4.681 秒,每秒能够解决 1068.04 个申请,而 FastAPI 破费 2.060 秒,每秒能够解决 2426.78 个申请。
结语:曾几何时,当人们议论 Python 框架的性能时,总是不盲目的不屑一顾,而当初,Python 异步生态正在产生着惊天动地的变动,新的框架应运而生(Sanic、FastAPI),旧的框架正在重构(Django3.0),很多库也开始反对异步(httpx、Sqlalchemy、Mortor)。软件科技倒退的历史表明,一项新技术的呈现和利用,经常会给这个畛域带来粗浅的改革,古语有云:察势者智,趁势者赢,驭势者独步天下。所以,只有拥抱将来、拥抱新技术、顺应时代才是正确的、可继续倒退的路线。
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_167