Durante l'integrazione di un servizio di backend con il bilanciatore del carico delle applicazioni, è importante misurare il rendimento di un servizio di backend da solo, in assenza di un bilanciatore del carico. I test di carico in condizioni controllate ti aiutano a valutare i compromessi della pianificazione della capacità tra diverse dimensioni del rendimento, come throughput e latenza. Poiché una pianificazione attenta della capacità potrebbe comunque sottostimare la domanda effettiva, ti consigliamo di utilizzare i test di carico per determinare in modo proattivo in che modo la disponibilità di un servizio è interessata quando il sistema è sovracaricato.
Obiettivi dei test di carico
Un test di carico tipico misura il comportamento visibile all'esterno del servizio di backend in diverse dimensioni di carico. Di seguito sono riportate alcune delle dimensioni più pertinenti di questi test:
- Throughput delle richieste:il numero di richieste gestite al secondo.
- Contemporaneità delle richieste:il numero di richieste elaborate contemporaneamente.
- Throughput della connessione: il numero di connessioni avviate dai client al secondo. La maggior parte dei servizi che utilizzano Transport Layer Security (TLS) presenta un certo ovvio overhead di trasporto di rete e negoziazione TLS associato a ogni connessione indipendente dall'elaborazione delle richieste.
Contemporaneità delle connessioni:il numero di connessioni client elaborate contemporaneamente.
Latenza della richiesta:il tempo totale trascorso tra l'inizio della richiesta e la fine della risposta.
Tasso di errori: la frequenza con cui le richieste causano errori, ad esempio errori HTTP 5xx e chiusure premature delle connessioni.
Per valutare lo stato del server sotto carico, una procedura di test di carico potrebbe anche raccogliere le seguenti metriche del servizio interno:
Utilizzo delle risorse di sistema: le risorse di sistema, come CPU, RAM e handle file (socket), sono in genere espresse in percentuale.
L'importanza di queste metriche varia in base a come viene implementato il servizio. Le applicazioni presentano prestazioni ridotte, riducono il carico o si arrestano in modo anomalo quando esauriscono le risorse. Pertanto, diventa essenziale determinare la disponibilità delle risorse quando un host è sotto carico elevato.
Utilizzo di altre risorse limitate:risorse non di sistema che potrebbero essere esaurite sotto carico, ad esempio a livello di applicazione.
Ecco alcuni esempi di risorse di questo tipo:
- Un pool delimitato di thread o processi worker.
- Per un server applicazioni che utilizza thread, è comune limitare il numero di thread worker in esecuzione contemporaneamente. I limiti di dimensione del pool di thread sono utili per evitare l'esaurimento della memoria e della CPU, ma le impostazioni predefinite sono spesso molto conservative. Limiti troppo bassi potrebbero impedire un utilizzo adeguato delle risorse di sistema.
- Alcuni server utilizzano pool di processi anziché pool di thread. Ad esempio, un server Apache configurato con il modello di pre-elaborazione multi-tasking, assegna un processo a ogni connessione client. Pertanto, il limite di dimensione del pool determina il limite superiore alla concorrenza delle connessioni.
- Un servizio di cui è stato eseguito il deployment come frontend di un altro servizio che ha un pool di connessioni di backend di dimensioni limitate.
Pianificazione della capacità rispetto ai test di sovraccarico
Gli strumenti di test di carico ti aiutano a misurare singolarmente le diverse dimensioni di scalabilità. Per la pianificazione della capacità, determina la soglia di carico per il rendimento accettabile in più dimensioni. Ad esempio, anziché misurare il limite assoluto di una richiesta di servizio, valuta la possibilità di misurare quanto segue:
- La tasso di richieste con cui il servizio può essere pubblicato con una latenza del 99° percentile inferiore a un numero specificato di millisecondi. Il numero è specificato dallo SLO del servizio.
- La frequenza massima di richieste che non causa il superamento dei livelli ottimali di utilizzo delle risorse di sistema. Tieni presente che l'utilizzo ottimale varia in base all'applicazione e potrebbe essere notevolmente inferiore al 100%. Ad esempio, con un picco di utilizzo della memoria all'80%, l'applicazione potrebbe essere in grado di gestire picchi di carico minori meglio che se l'utilizzo di picco fosse al 99%.
Sebbene sia importante utilizzare i risultati dei test di carico per prendere decisioni relative alla pianificazione della capacità, è altrettanto importante capire il comportamento di un servizio quando il carico supera la capacità. Di seguito sono riportati alcuni comportamenti del server spesso valutati utilizzando i test di sovraccarico:
Ridondanza: quando un servizio riceve richieste o connessioni in entrata eccessive, potrebbe rispondere rallentando tutte le richieste o rifiutandone alcune per mantenere un rendimento accettabile per le rimanenti. Consigliamo di adottare quest'ultimo approccio per evitare i timeout del client prima di ricevere una risposta e per ridurre il rischio di esaurimento della memoria diminuendo la concorrenza delle richieste sul server.
Resilienza all'esaurimento delle risorse: in genere un servizio evita di bloccarsi per esaurimento delle risorse perché è difficile per le richieste in attesa fare ulteriori progressi se il servizio si è bloccato. Se un servizio di backend ha molte istanze, la robustezza delle singole istanze è fondamentale per la disponibilità complessiva del servizio. Quando un'istanza si riavvia dopo un arresto anomalo, altre istanze potrebbero subire un carico maggiore, causando potenzialmente un errore a cascata.
Linee guida generali per i test
Quando definisci i casi di test, tieni presenti le seguenti linee guida.
Creare test su piccola scala
Crea test su piccola scala per misurare i limiti di prestazioni del server. Con una capacità eccessiva del server, c'è il rischio che un test non riveli i limiti di prestazioni del servizio stesso, ma potrebbe rilevare colli di bottiglia in altri sistemi, come gli host client o il livello di rete.
Per risultati ottimali, valuta un caso di test che utilizzi una singola istanza di macchina virtuale (VM) o un pod Google Kubernetes Engine (GKE) per testare in modo indipendente il servizio. Per ottenere il carico completo sul server, se necessario, puoi utilizzare più VM, ma ricorda che possono complicare la raccolta dei dati sulle prestazioni.
Scegliere i pattern di carico a ciclo aperto
La maggior parte dei generatori di carico utilizza il pattern a ciclo chiuso per limitare il numero di richieste simultanee e ritardare le nuove richieste fino al completamento di quelle precedenti. Sconsigliamo questo approccio perché i client di produzione del servizio potrebbero non mostrare questo comportamento di throttling.
Al contrario, il pattern a ciclo aperto consente ai generatori di carico di simulare il carico di produzione inviando richieste a una frequenza costante, indipendentemente dalla frequenza con cui arrivano le risposte del server.
Esegui i test utilizzando i generatori di carico consigliati
Per i test di carico del servizio di backend, consigliamo i seguenti generatori di carico:
Nighthawk
Nighthawk è uno strumento open source sviluppato in collaborazione con il progetto Envoy. Puoi usarlo per generare il carico del client, visualizzare i benchmark e misurare le prestazioni del server per la maggior parte degli scenari di test di carico dei servizi HTTPS.
Test HTTP/1
Per testare HTTP/1, utilizza il seguente comando:
nighthawk_client URI \ --duration DURATION \ --open-loop \ --no-default-failure-predicates \ --protocol http1 \ --request-body-size REQ_BODY_SIZE \ --concurrency CONCURRENCY \ --rps RPS \ --connections CONNECTIONS
Sostituisci quanto segue:
URI
: l'URI da sottoporre a benchmarkDURATION
: tempo di esecuzione totale del test in secondiREQ_BODY_SIZE
: dimensioni del payload POST in ogni richiestaCONCURRENCY
: il numero totale di loop di eventi concorrenzialiQuesto numero deve corrispondere al numero di core della VM client
RPS
: la frequenza target delle richieste al secondo, per loop di eventiCONNECTIONS
: il numero di connessioni simultanee per loop di eventi
Vedi il seguente esempio:
nighthawk_client http://10.20.30.40:80 \ --duration 600 --open-loop --no-default-failure-predicates \ --protocol http1 --request-body-size 5000 \ --concurrency 16 --rps 500 --connections 200
L'output di ogni esecuzione del test fornisce un'istogramma delle latenze di risposta. Nell'esempio della documentazione di Nighthawk, tieni presente che la latenza del 99° percentile è di circa 135 microsecondi.
Initiation to completion samples: 9992 mean: 0s 000ms 113us pstdev: 0s 000ms 061us Percentile Count Latency 0 1 0s 000ms 077us 0.5 4996 0s 000ms 115us 0.75 7495 0s 000ms 118us 0.8 7998 0s 000ms 118us 0.9 8993 0s 000ms 121us 0.95 9493 0s 000ms 124us 0.990625 9899 0s 000ms 135us 0.999023 9983 0s 000ms 588us 1 9992 0s 004ms 090us
Testare HTTP/2
Per testare HTTP/2, utilizza il seguente comando:
nighthawk_client URI \ --duration DURATION \ --open-loop \ --no-default-failure-predicates \ --protocol http2 \ --request-body-size REQ_BODY_SIZE \ --concurrency CONCURRENCY \ --rps RPS \ --max-active-requests MAX_ACTIVE_REQUESTS \ --max-concurrent-streams MAX_CONCURRENT_STREAMS
Sostituisci quanto segue:
URI
: l'URI da sottoporre a benchmarkDURATION
: tempo di esecuzione totale del test in secondiREQ_BODY_SIZE
: dimensioni del payload POST in ogni richiestaCONCURRENCY
: il numero totale di loop di eventi concorrenzialiQuesto numero deve corrispondere al numero di core della VM client
RPS
: la frequenza target delle richieste al secondo per ogni loop di eventiMAX_ACTIVE_REQUESTS
: il numero massimo di richieste attive contemporanee per ogni loop di eventiMAX_CONCURRENT_STREAMS
: il numero massimo di stream contemporaneamente consentiti su ogni connessione HTTP/2
Vedi il seguente esempio:
nighthawk_client http://10.20.30.40:80 \ --duration 600 --open-loop --no-default-failure-predicates \ --protocol http2 --request-body-size 5000 \ --concurrency 16 --rps 500 \ --max-active-requests 200 --max-concurrent-streams 1
ab (strumento di benchmark Apache)
ab
è un'alternativa meno flessibile a Nighthawk, ma è disponibile come pacchetto su
quasi tutte le distribuzioni Linux. ab
è consigliato solo per test rapidi e semplici.
Per installare ab
, utilizza il seguente comando:
- Su Debian e Ubuntu, esegui
sudo apt-get install apache2-utils
. - Nelle distribuzioni basate su RedHat, esegui
sudo yum install httpd-utils
.
Dopo aver installato ab
, utilizza il seguente comando per eseguirlo:
ab -c CONCURRENCY \ -n NUM_REQUESTS \ -t TIMELIMIT \ -p POST_FILE URI
Sostituisci quanto segue:
CONCURRENCY
: numero di richieste simultanee da eseguireNUM_REQUESTS
: numero di richieste da eseguireTIMELIMIT
: numero massimo di secondi da dedicare alle richiestePOST_FILE
: file locale contenente il payload del POST HTTPURI
: l'URI da sottoporre a benchmark
Vedi il seguente esempio:
ab -c 200 -n 1000000 -t 600 -P body http://10.20.30.40:80
Il comando nell'esempio precedente invia richieste con una concorrenza di 200
(pattern a ciclo chiuso) e si interrompe dopo 1.000.000 (un milione) di richieste
o 600 secondi di tempo trascorso. Il comando include anche i contenuti del
file body
come payload POST HTTP.
Il comando ab
genera istogrammi della latenza di risposta simili a quelli di Nighthawk, ma la sua risoluzione è limitata ai millisecondi anziché ai microsecondi:
Percentage of the requests served within a certain time (ms) 50% 7 66% 7 75% 7 80% 7 90% 92 95% 121 98% 123 99% 127 100% 156 (longest request)