2020年是時候更新你的技術(shù)武器庫了:Asgi vs Wsgi(FastAPI vs Flask)

原文轉(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簡單工作原理示意圖:

image

簡單實現(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簡單工作原理示意圖:

image

簡單實現(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:

image

FastAPI:

image

可以看到歼冰,同樣的四次請求靡狞,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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末华畏,一起剝皮案震驚了整個濱河市鹏秋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌亡笑,老刑警劉巖侣夷,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異仑乌,居然都是意外死亡百拓,警方通過查閱死者的電腦和手機(jī)琴锭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衙传,“玉大人决帖,你說我怎么就攤上這事”痛罚” “怎么了地回?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長俊鱼。 經(jīng)常有香客問我刻像,道長,這世上最難降的妖魔是什么并闲? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任绎速,我火速辦了婚禮,結(jié)果婚禮上焙蚓,老公的妹妹穿的比我還像新娘纹冤。我一直安慰自己,他們只是感情好购公,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布萌京。 她就那樣靜靜地躺著,像睡著了一般宏浩。 火紅的嫁衣襯著肌膚如雪知残。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天比庄,我揣著相機(jī)與錄音求妹,去河邊找鬼。 笑死佳窑,一個胖子當(dāng)著我的面吹牛制恍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播神凑,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼净神,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了溉委?” 一聲冷哼從身側(cè)響起鹃唯,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瓣喊,沒想到半個月后坡慌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡藻三,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年洪橘,在試婚紗的時候發(fā)現(xiàn)自己被綠了絮爷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡梨树,死狀恐怖坑夯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抡四,我是刑警寧澤柜蜈,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站指巡,受9級特大地震影響淑履,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜藻雪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一秘噪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧勉耀,春花似錦指煎、人聲如沸每币。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽致扯。三九已至枢纠,卻和暖如春像街,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背晋渺。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工镰绎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人木西。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓畴栖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親户魏。 傳聞我的和親對象是個殘疾皇子驶臊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355