原文轉(zhuǎn)載自「劉悅的技術(shù)博客」https://v3u.cn/a_id_167
也許這一篇的標(biāo)題有那么一點(diǎn)不厚道行楞,因為Asgi(Asynchronous Server Gateway Interface)畢竟是Wsgi(Web Server Gateway Interface)的擴(kuò)展,而FastAPI畢竟也是站在Flask的肩膀上才有了突飛猛進(jìn)的發(fā)展钧栖,大多數(shù)人聽說Asgi也許是因為Django的最新版(3.0)早已宣布支持Asgi網(wǎng)絡(luò)規(guī)范洛二,這顯然是一個振奮人心的消息奸绷,2020年锁施,如果你在Web開發(fā)面試中不扯一點(diǎn)Asgi墩剖,顯然就有點(diǎn)落后于形勢了。
那么到底啥是Wsgi痒筒,什么又是Asgi宰闰,放心,不扯CGI凸克,不扯各種抽象概念议蟆,簡單粗暴理解:
Wsgi是同步通信服務(wù)規(guī)范闷沥,客戶端請求一項服務(wù)萎战,并等待服務(wù)完成,只有當(dāng)它收到服務(wù)的結(jié)果時舆逃,它才會繼續(xù)工作蚂维。當(dāng)然了,可以定義一個超時時間路狮,如果服務(wù)在規(guī)定的時間內(nèi)沒有完成虫啥,則認(rèn)為調(diào)用失敗,調(diào)用方繼續(xù)工作奄妨。
Wsgi簡單工作原理示意圖:
簡單實現(xiàn):
#WSGI example
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return b'Hello, Wsgi\n'
Asgi是異步通信服務(wù)規(guī)范涂籽。客戶端發(fā)起服務(wù)呼叫砸抛,但不等待結(jié)果评雌。調(diào)用方立即繼續(xù)其工作,并不關(guān)心結(jié)果直焙。如果調(diào)用方對結(jié)果感興趣景东,有一些機(jī)制可以讓其隨時被回調(diào)方法返回結(jié)果。
Asgi簡單工作原理示意圖:
簡單實現(xiàn):
#Asgi example
async def application(scope, receive, send):
event = await receive()
...
await send({"type": "websocket.send", ...})
簡單總結(jié)一下:Asgi是異步的奔誓,Wsgi是同步的斤吐,而基于Wsgi的Flask是同步框架,基于Asgi的FastAPI是異步框架,就這么簡單和措,那么同步框架和異步框架的區(qū)別到底在哪兒庄呈?為什么要把Flask換成FastAPI?
不靠拍腦門兒、也不是道聽途說派阱、人云亦云抒痒。玩技術(shù)的應(yīng)該用數(shù)據(jù)說話,論點(diǎn)永遠(yuǎn)依托論據(jù)颁褂,所以我們來簡單對兩款框架的性能做一個測試故响,首先分別安裝依賴的庫。
Flask:
pip install gunicorn
pip install gevent
pip install flask
FastAPI:
pip install fastapi
pip install uvicorn
我們首先干的一件事就是颁独,看看Flask和FastAPI如何處理來自多個客戶端的多個請求彩届。特別是當(dāng)代碼存在效率問題時(比如數(shù)據(jù)庫查詢時間長這種耗時任務(wù)),這里故意使用time.sleep()來模擬耗時任務(wù)誓酒,為什么不用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)
分別啟動服務(wù)
Flask:python3 manage.py
FastAPI:uvicorn manage:app --reload
同時一時間內(nèi)靠柑,開啟多個瀏覽器寨辩,分別并發(fā)請求首頁 。
Flask:http://localhost:5000
FastAPI:http://localhost:8000
觀察后臺打印結(jié)果:
Flask:
FastAPI:
可以看到歼冰,同樣的四次請求靡狞,F(xiàn)lask先是阻塞了40秒,然后依次返回結(jié)果隔嫡,F(xiàn)astAPI則是第一次阻塞后直接返回甸怕,這代表了在FastAPI中阻塞了一個事件隊列,證明FastAPI是異步框架腮恩,而在Flask中梢杭,請求可能是在新線程中運(yùn)行的。將所有CPU綁定的任務(wù)移到單獨(dú)的進(jìn)程中秸滴,所以在FastAPI的例子中武契,只是在事件循環(huán)中sleep(所以異步框架這里最好不要使用time.sleep而是asyncio.sleep)。在FastAPI中荡含,異步運(yùn)行IO綁定的任務(wù)咒唆。
當(dāng)然這不能說明太多問題,我們繼續(xù)使用鼎鼎有名的ApacheBench分別對兩款框架進(jìn)行壓測内颗。
一共設(shè)置5000個請求钧排,QPS是100(請原諒我的機(jī)器比較渣)。
ab -n 5000 -c 100 http://127.0.0.1:5000/
ab -n 5000 -c 100 http://127.0.0.1:8000/
這里為了公平起見均澳,F(xiàn)lask配合Gunicorn服務(wù)器恨溜,開3個worker符衔,F(xiàn)astAPI配合Uvicorn服務(wù)器,同樣開3個worker糟袁。
Flask壓測結(jié)果:
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壓測結(jié)果:
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個總請求,F(xiàn)lask花費(fèi)4.681秒项戴,每秒可以處理1068.04個請求形帮,而FastAPI花費(fèi)2.060秒,每秒可以處理2426.78個請求周叮。
結(jié)語:曾幾何時辩撑,當(dāng)人們談?wù)揚(yáng)ython框架的性能時,總是不自覺的嗤之以鼻 仿耽,而現(xiàn)在合冀,Python異步生態(tài)正在發(fā)生著驚天動地的變化,新的框架應(yīng)運(yùn)而生(Sanic项贺、FastAPI)君躺,舊的框架正在重構(gòu)(Django3.0),很多庫也開始支持異步(httpx开缎、Sqlalchemy棕叫、Mortor)。軟件科技發(fā)展的歷史表明奕删,一項新技術(shù)的出現(xiàn)和應(yīng)用俺泣,常常會給這個領(lǐng)域帶來深刻的變革,古語有云:察勢者智急侥,順勢者贏砌滞,馭勢者獨(dú)步天下侮邀。所以坏怪,只有擁抱未來、擁抱新技術(shù)绊茧、順應(yīng)時代才是正確的铝宵、可持續(xù)發(fā)展的道路。
原文轉(zhuǎn)載自「劉悅的技術(shù)博客」 https://v3u.cn/a_id_167