Sentiti libero di condividere i nostri tutorial e codici sui tuoi social network, abbiamo bisogno del tuo aiuto per espandere questa community.
Soluzione:
Questo sembrava un po' interessante, quindi ho fatto dei piccoli test con ApacheBench
:
Fiasco
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class Root(Resource):
def get(self):
return {"message": "hello"}
api.add_resource(Root, "/")
FastAPI
from fastapi import FastAPI
app = FastAPI(debug=False)
@app.get("/")
async def root():
return {"message": "hello"}
Ho eseguito 2 test per FastAPI, con un'enorme differenza:
gunicorn -w 4 -k uvicorn.workers.UvicornWorker fast_api:app
uvicorn fast_api:app --reload
Ecco i risultati del benchmarking per 5000 richieste con una concurrency di 500:
FastAPI con i lavoratori Uvicorn
Concurrency Level: 500
Time taken for tests: 0.577 seconds
Complete requests: 5000
Failed requests: 0
Total transferred: 720000 bytes
HTML transferred: 95000 bytes
Requests per second: 8665.48 [#/sec] (mean)
Time per request: 57.700 [ms] (mean)
Time per request: 0.115 [ms] (mean, across all concurrent requests)
Transfer rate: 1218.58 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 6 4.5 6 30
Processing: 6 49 21.7 45 126
Waiting: 1 42 19.0 39 124
Total: 12 56 21.8 53 127
Percentage of the requests served within a certain time (ms)
50% 53
66% 64
75% 69
80% 73
90% 81
95% 98
98% 112
99% 116
100% 127 (longest request)
FastAPI - Uvicorn puro
Concurrency Level: 500
Time taken for tests: 1.562 seconds
Complete requests: 5000
Failed requests: 0
Total transferred: 720000 bytes
HTML transferred: 95000 bytes
Requests per second: 3200.62 [#/sec] (mean)
Time per request: 156.220 [ms] (mean)
Time per request: 0.312 [ms] (mean, across all concurrent requests)
Transfer rate: 450.09 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 8 4.8 7 24
Processing: 26 144 13.1 143 195
Waiting: 2 132 13.1 130 181
Total: 26 152 12.6 150 203
Percentage of the requests served within a certain time (ms)
50% 150
66% 155
75% 158
80% 160
90% 166
95% 171
98% 195
99% 199
100% 203 (longest request)
Per Flask:
Concurrency Level: 500
Time taken for tests: 27.827 seconds
Complete requests: 5000
Failed requests: 0
Total transferred: 830000 bytes
HTML transferred: 105000 bytes
Requests per second: 179.68 [#/sec] (mean)
Time per request: 2782.653 [ms] (mean)
Time per request: 5.565 [ms] (mean, across all concurrent requests)
Transfer rate: 29.13 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 87 293.2 0 3047
Processing: 14 1140 4131.5 136 26794
Waiting: 1 1140 4131.5 135 26794
Total: 14 1227 4359.9 136 27819
Percentage of the requests served within a certain time (ms)
50% 136
66% 148
75% 179
80% 198
90% 295
95% 7839
98% 14518
99% 27765
100% 27819 (longest request)
Risultati totali
Fiasco: Tempo impiegato per i test: 27,827 secondi
FastAPI - Uvicorn: Tempo impiegato per i test: 1,562 secondi
Lavoratori FastAPI - Uvicorn: Tempo impiegato per i test: 0,577 secondi
Con Uvicorn Workers FastAPI è quasi 48x più veloce di Flask, il che è molto comprensibile. ASGI vs WSGI Ho eseguito il test con 1 concurrenza:
FastAPI - UvicornWorkers: Tempo impiegato per i test: 1,615 secondi
FastAPI - Uvicorn puro: Tempo impiegato per i test: 2,681 secondi
Fiasco: Tempo impiegato per i test: 5,541 secondi
Ho eseguito altri test per verificare Flask con un server di produzione.
5000 Richieste 1000 Concorrenze
Flask con Waitress
Server Software: waitress
Server Hostname: 127.0.0.1
Server Port: 8000
Document Path: /
Document Length: 21 bytes
Concurrency Level: 1000
Time taken for tests: 3.403 seconds
Complete requests: 5000
Failed requests: 0
Total transferred: 830000 bytes
HTML transferred: 105000 bytes
Requests per second: 1469.47 [#/sec] (mean)
Time per request: 680.516 [ms] (mean)
Time per request: 0.681 [ms] (mean, across all concurrent requests)
Transfer rate: 238.22 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 4 8.6 0 30
Processing: 31 607 156.3 659 754
Waiting: 1 607 156.3 658 753
Total: 31 611 148.4 660 754
Percentage of the requests served within a certain time (ms)
50% 660
66% 678
75% 685
80% 691
90% 702
95% 728
98% 743
99% 750
100% 754 (longest request)
Gunicorn con lavoratori Uvicorn
Server Software: uvicorn
Server Hostname: 127.0.0.1
Server Port: 8000
Document Path: /
Document Length: 19 bytes
Concurrency Level: 1000
Time taken for tests: 0.634 seconds
Complete requests: 5000
Failed requests: 0
Total transferred: 720000 bytes
HTML transferred: 95000 bytes
Requests per second: 7891.28 [#/sec] (mean)
Time per request: 126.722 [ms] (mean)
Time per request: 0.127 [ms] (mean, across all concurrent requests)
Transfer rate: 1109.71 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 28 13.8 30 62
Processing: 18 89 35.6 86 203
Waiting: 1 75 33.3 70 171
Total: 20 118 34.4 116 243
Percentage of the requests served within a certain time (ms)
50% 116
66% 126
75% 133
80% 137
90% 161
95% 189
98% 217
99% 230
100% 243 (longest request)
Uvicorno puro, ma questa volta 4 lavoratori uvicorn fastapi:app --workers 4
Server Software: uvicorn
Server Hostname: 127.0.0.1
Server Port: 8000
Document Path: /
Document Length: 19 bytes
Concurrency Level: 1000
Time taken for tests: 1.147 seconds
Complete requests: 5000
Failed requests: 0
Total transferred: 720000 bytes
HTML transferred: 95000 bytes
Requests per second: 4359.68 [#/sec] (mean)
Time per request: 229.375 [ms] (mean)
Time per request: 0.229 [ms] (mean, across all concurrent requests)
Transfer rate: 613.08 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 20 16.3 17 70
Processing: 17 190 96.8 171 501
Waiting: 3 173 93.0 151 448
Total: 51 210 96.4 184 533
Percentage of the requests served within a certain time (ms)
50% 184
66% 209
75% 241
80% 260
90% 324
95% 476
98% 504
99% 514
100% 533 (longest request)
State usando il time.sleep()
in una funzione async
endpoint. time.sleep()
è bloccante e non dovrebbe mai essere usato nel codice asincrono. Quello che si dovrebbe usare è probabilmente il metodo asyncio.sleep()
:
import asyncio
import uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
async def root():
print('Sleeping for 10')
await asyncio.sleep(10)
print('Awake')
return {'message': 'hello'}
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)
In questo modo, ogni richiesta impiegherà ~10 secondi per essere completata, ma si sarà in grado di eseguire più richieste simultaneamente.
In generale, i framework asincroni offrono sostituzioni per tutte le funzioni bloccanti all'interno della libreria standard (funzioni sleep, funzioni IO, ecc.). Si devono usare questi sostituti quando si scrive codice asincrono e (facoltativamente) await
loro.
Alcuni framework e librerie non bloccanti, come gevent, non offrono sostituzioni. Invece scimmiottano le funzioni della libreria standard per renderle non bloccanti. Questo non è il caso, per quanto ne so, dei framework e delle librerie async più recenti, perché sono pensati per consentire allo sviluppatore di usare la sintassi async-await.
Penso che si stia bloccando una coda di eventi in FastAPI, che è un framework asincrono, mentre in Flask le richieste vengono probabilmente eseguite ciascuna in un nuovo thread. Spostate tutte le attività legate alla CPU in processi separati o, nell'esempio di FastAPI, dormite solo sul ciclo degli eventi (non usate time.sleep qui). In FastAPI, i task legati all'IO vengono eseguiti in modo asincrono.
Ricorda che hai la concessione di dire se colpisci la tua battuta d'arresto in tempo.