Confronto di server REST ad alte prestazioni
Introduzione
Quando si tratta di confrontare le prestazioni del puro instradamento di web server "vuoti", il riferimento standard è web-frameworks.
Ci siamo chiesti come variassero questi risultati con una comunicazione REST HTTP elementare con un minimo di calcolo a carico del server, non una semplice richiesta con risposta vuota.
Testa o croce
Abbiamo quindi ipotizzato di realizzare un endpoint elementarare del tipo
- il client invia via POST la propria scommessa (valore booleano di
bet
, pari a testa o croce) - il server estrae un booleano casuale e restituisce
{ win: true }
nel caso coincida con la scommessa ricevuta,false
altrimenti
I framework scelti
I linguaggi di programmazione sono moltissimi e, per filtrare fortemente, abbiamo deciso di utilizzare un criterio per limitarsi ai linguaggi molto maturi e diffusi: l'intersezione tra quelli
- supportati dall'SDK di AWS (C++, Go, Java, JavaScript/Node.js, .NET, PHP, Python, Ruby)
- che noi frequentiamo tra i suddetti (Go, JavaScript/Node.js, PHP, Python, Ruby)
Per ciascun linguaggio, abbiamo scelto un rappresentante, non necessariamente il più veloce perché ci interessava una variazione di prestazioni e non un valore assoluto e quindi spesso i criteri son stati anche di semplicità, documentazione e diffusione, oltre alla conoscenza pregressa.
- PHP => Mark è il primo dei quasi 200 framework: basato su workerman, usa un server autonomo (non ha bisogno di NGINX o Apache) e "forka" diversi processi;
- Go => Gin (in web-frameworks, il framework Go più veloce è Fiber);
- JavaScript => Fastify (escludendo quelli nativi per WebSocket, il primo è 0http);
- Python => FastAPI (il primo dell'elenco era Falcon);
- Ruby => Rack, essendo la base sulla quale, in Ruby, sono basati molto famosi framewor, mentre il più veloce, pur basato su Rack, è Agoo,
Risultati
Abbiamo provato su AWS EC2 con una istanza c5a.xlarge
, eseguendo chiamate locali con wrk, lo stesso client HTTP di benchmarking usato da web-frameworks, lanciando
wrk -t12 -c400 -d120s -s post.lua http://0.0.0.0:3000/coin
dove lo script Lua è
wrk.method = "POST"
wrk.body = '{"bet": true}'
wrk.headers["Content-Type"] = "application/json"
Ecco i risultati in richieste al secondo servite nel caso "vuoto" di web-frameworks e nel caso della moneta. Abbiamo poi normalizzato le prestazioni con uno Score rispetto a Rack.
r/s void | r/s coin | Score void | Score coin | |
---|---|---|---|---|
Rack (Ruby) | 19318 | 8869 | 1,00 | 1,00 |
FastAPI (Python) | 26078 | 2997 | 1,35 | 0,34 |
Fastify (Node.js) | 69627 | 20161 | 3,60 | 2,27 |
Gin (Go) | 99998 | 49093 | 5,18 | 5,54 |
Mark (PHP) | 167687 | 141205 | 8,68 | 15,92 |
La tabella può essere resa più evidente nel grafico seguente (diagramma rosso per la moneta, blu per il caso vuoto).
Conclusioni
Rispetto al rappresentante di Ruby, i rappresentanti di Python e Node.js sembrano peggiorare le proprie prestazioni: Go e quel particolare PHP si comportano molto bene a fronte di una maggiore complessità da gestire, pur essendo comunque un esercizio e non un vero applicativo. È inoltre interessante notare come, pur essendo Mark primo, dei 10 migliori framework ben 6 siano in Go.
Il codice del client e dei server è disponibile su GitHub nel repository rest-coin-benchmark.
- Photo by Nicolas Hoizey on Unsplash
- Photo by Jonathan Chng on Unsplash