Skip to content

Architettura Flask vs FastAPI

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:

  1. gunicorn -w 4 -k uvicorn.workers.UvicornWorker fast_api:app
  2. 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.



Utilizzate il nostro motore di ricerca

Ricerca
Generic filters

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.