原文轉載自「劉悅的技術博客」v3u.cn/a_id_167python
也許這一篇的標題有那麼一點不厚道,由於Asgi(Asynchronous Server Gateway Interface)畢竟是Wsgi(Web Server Gateway Interface)的擴展,而FastAPI畢竟也是站在Flask的肩膀上纔有了日新月異的發展,大多數人據說Asgi也許是由於Django的最新版(3.0)早已宣佈支持Asgi網絡規範,這顯然是一個振奮人心的消息,2020年,若是你在Web開發面試中不扯一點Asgi,顯然就有點落後於形勢了。web
那麼到底啥是Wsgi,什麼又是Asgi,放心,不扯CGI,不扯各類抽象概念,簡單粗暴理解:面試
Wsgi是同步通訊服務規範,客戶端請求一項服務,並等待服務完成,只有當它收到服務的結果時,它纔會繼續工做。固然了,能夠定義一個超時時間,若是服務在規定的時間內沒有完成,則認爲調用失敗,調用方繼續工做。數據庫
Wsgi簡單工做原理示意圖:apache
簡單實現:flask
#WSGI example
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return b'Hello, Wsgi\n'
複製代碼
Asgi是異步通訊服務規範。客戶端發起服務呼叫,但不等待結果。調用方當即繼續其工做,並不關心結果。若是調用方對結果感興趣,有一些機制可讓其隨時被回調方法返回結果。api
Asgi簡單工做原理示意圖:瀏覽器
簡單實現:bash
#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)。軟件科技發展的歷史代表,一項新技術的出現和應用,經常會給這個領域帶來深入的變革,古語有云:察勢者智,順勢者贏,馭勢者獨步天下。因此,只有擁抱將來、擁抱新技術、順應時代纔是正確的、可持續發展的道路。
原文轉載自「劉悅的技術博客」 v3u.cn/a_id_167