Richtlinien für Lasttest-Backend-Dienste mit Anwendungs-Load-Balancern

Wenn Sie einen Backend-Dienst in einen Anwendungs-Load-Balancer einbinden, ist es wichtig, die Leistung eines Backend-Dienstes allein zu messen, wenn kein Load-Balancer vorhanden ist. Mit Lasttests unter kontrollierten Bedingungen können Sie Kompromisse bei der Kapazitätsplanung zwischen verschiedenen Leistungsdimensionen wie Durchsatz und Latenz prüfen. Da eine sorgfältige Kapazitätsplanung die tatsächliche Nachfrage weiterhin unterschätzen kann, empfehlen wir, über Lasttests proaktiv zu bestimmen, wie sich die Verfügbarkeit eines Dienstes auf die Überlastung auswirkt.

Lasttestziele

Bei einem typischen Lasttest wird das extern sichtbare Verhalten des Backend-Dienstes unter verschiedenen Lastdimensionen gemessen. Im Folgenden sind einige der relevantesten Dimensionen dieses Tests aufgeführt:

  • Anfragedurchsatz: Die Anzahl der pro Sekunde verarbeiteten Anfragen.
  • Gleichzeitigkeit der Anfrage: Die Anzahl der gleichzeitig verarbeiteten Anfragen.
  • Verbindungsdurchsatz: Die Anzahl der von Clients pro Sekunde initiierten Verbindungen. Die meisten Dienste, die Transport Layer Security (TLS) verwenden, haben für jede Verbindung, die unabhängig von der Anfrageverarbeitung ist, einen gewissen Netzwerktransport- und TLS-Verhandlungsaufwand.
  • Gleichzeitigkeit der Verbindung: Die Anzahl der gleichzeitig verarbeiteten Clientverbindungen.

  • Anfragelatenz: Die Gesamtzeit zwischen dem Beginn der Anfrage und dem Ende der Antwort.

  • Fehlerrate: Gibt an, wie oft Anfragen Fehler verursachen, darunter HTTP 5xx-Fehler und vorzeitig geschlossene Verbindungen.

Zur Bewertung des Zustands des Servers unter Last kann ein Lasttestverfahren auch folgende interne Dienstmesswerte erfassen:

  • Nutzung von Systemressourcen: Systemressourcen wie CPU, RAM und Datei-Handles (Sockets) werden normalerweise in Prozent ausgedrückt.

    Die Bedeutung dieser Messwerte hängt davon ab, wie der Dienst implementiert wird. Wenn Anwendungen ihre Ressourcen aufbrauchen, kommt es zu einer geringeren Leistung, einem Lastverlust oder einem Absturz. Daher ist es wichtig, die Verfügbarkeit von Ressourcen zu bestimmen, wenn ein Host stark ausgelastet ist.

  • Nutzung anderer begrenzter Ressourcen: Nicht-Systemressourcen, die unter Last erschöpft sind, z. B. auf Anwendungsebene.

    Beispiele für solche Ressourcen:

    • Ein begrenzter Pool von Worker-Threads oder -Prozessen.
    • Bei einem Anwendungsserver, der Threads verwendet, ist es üblich, die Anzahl der gleichzeitig ausgeführten Worker-Threads zu begrenzen. Thread-Pool-Größenlimits sind nützlich, um zu verhindern, dass Arbeitsspeicher und CPU überlastet werden. Die Standardeinstellungen sind jedoch oft sehr konservativ. Zu niedrige Limits können eine angemessene Nutzung von Systemressourcen verhindern.
    • Einige Server verwenden Prozesspools anstelle von Threadpools. Beispiel: Ein Apache-Server weist bei Einrichtung mit dem Prefork Multi-Processing-Modell jeder Clientverbindung einen Prozess zu. Die Größenbeschränkung des Pools bestimmt also die Obergrenze für die Gleichzeitigkeit von Verbindungen.
    • Ein Dienst, der als Frontend für einen anderen Dienst bereitgestellt wird, der einen Backend-Verbindungspool begrenzter Größe hat.

Kapazitätsplanung und Überlastungstests

Mit Lasttesttools können Sie verschiedene Skalierungsdimensionen einzeln messen. Bestimmen Sie für die Kapazitätsplanung den Lastschwellenwert für die akzeptable Leistung in mehreren Dimensionen. Anstatt beispielsweise das absolute Limit einer Dienstanfrage zu messen, sollten Sie Folgendes messen:

  • Die Anfragerate, mit der der Dienst mit einer Latenz des 99. Perzentils verarbeitet werden kann, die unter einer angegebenen Anzahl an Millisekunden liegt. Die Nummer wird durch das SLO des Dienstes angegeben.
  • Die maximale Anfragerate, die nicht dazu führt, dass die Systemressourcenauslastung die optimalen Werte überschreitet. Beachten Sie, dass die optimale Auslastung je nach Anwendung variiert und erheblich unter 100 % liegen kann. Bei einer Spitzenauslastung von 80 % kann die Anwendung beispielsweise kleinere Lastspitzen besser bewältigen als die Spitzenauslastung von 99 %.

Es ist zwar wichtig, die Ergebnisse von Lasttests zu verwenden, um Entscheidungen zur Kapazitätsplanung zu bilden, aber es ist ebenso wichtig zu verstehen, wie sich ein Dienst verhält, wenn die Last die Kapazität überschreitet. Einige Serververhalten, die häufig mithilfe von Überlastungstests ausgewertet werden, sind:

  • Lastverlust: Wenn ein Dienst übermäßig eingehende Anfragen oder Verbindungen empfängt, kann er reagieren, indem alle Anfragen verlangsamt werden oder einige Anfragen abgelehnt werden, um eine akzeptable Leistung für die verbleibenden zu erhalten. Wir empfehlen den zweiten Ansatz, um Zeitüberschreitungen des Clients vor dem Empfang einer Antwort zu verhindern und das Risiko einer Speicherausschöpfung durch die Verringerung der gleichzeitigen Anfrage auf dem Server zu reduzieren.

  • Sicherheit vor Ressourcenüberlastung: Ein Dienst vermeidet im Allgemeinen das Abstürzen der Ressourcen, da es für ausstehende Anfragen schwierig ist, weitere Fortschritte zu machen, wenn der Dienst abstürzt. Wenn ein Backend-Dienst viele Instanzen hat, ist die Robustheit einzelner Instanzen für die Gesamtverfügbarkeit des Dienstes von entscheidender Bedeutung. Während eine Instanz nach einem Absturz neu gestartet wird, kann es bei anderen Instanzen zu einer höheren Last kommen. Dies kann zu einem Kaskadierungsfehler führen.

Allgemeine Testrichtlinien

Berücksichtigen Sie bei der Definition Ihrer Testfälle folgende Richtlinien.

Kleine Tests erstellen

Erstellen Sie kleine Tests, um die Leistungsgrenzen des Servers zu messen. Bei übermäßiger Serverkapazität besteht das Risiko, dass ein Test nicht die Leistungsgrenzen des Dienstes selbst aufdeckt, sondern Engpässe in anderen Systemen erkennt, z. B. in den Clienthosts oder in der Netzwerkebene.

Die besten Ergebnisse erzielen Sie, wenn Sie einen Testfall verwenden, bei dem eine einzelne VM-Instanz oder ein Google Kubernetes Engine-Pod (GKE) verwendet wird, um den Dienst unabhängig zu testen. Bei Bedarf können Sie mehrere VMs verwenden, um die vollständige Last auf dem Server zu erreichen. Beachten Sie jedoch, dass die Erfassung der Leistungsdaten kompliziert werden kann.

Open-Loop-Lastmuster auswählen

Die meisten Load Generators verwenden das Closed-Loop-Muster, um die Anzahl der gleichzeitigen Anfragen zu begrenzen und neue Anfragen zu verzögern, bis die vorherigen abgeschlossen sind. Wir raten von diesem Ansatz jedoch ab, da die Produktionsclients des Dienstes möglicherweise kein solches Drosselungsverhalten aufweisen.

Im Gegensatz dazu ermöglicht es das Open-Loop-Muster Load-Generatoren, die Produktionslast zu simulieren. Dazu werden Anfragen mit einer konstanten Rate gesendet, unabhängig von der Rate, mit der Serverantworten eingehen.

Wir empfehlen folgende Lastgeneratoren für die Lasttests des Backend-Dienstes:

Nighthawk

Nighthawk ist ein Open-Source-Tool, das in Zusammenarbeit mit dem Envoy-Projekt entwickelt wurde. Sie können damit Client-Lasten generieren, Benchmarks visualisieren und die Serverleistung für die meisten Lasttest-Szenarien der HTTPS-Dienste messen.

HTTP/1 testen

Testen Sie HTTP/1 mit folgendem Befehl:

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

Ersetzen Sie Folgendes:

  • URI: Die zu vergleichende URI
  • DURATION: Die gesamte Testlaufzeit in Sekunden
  • REQ_BODY_SIZE: Größe der POST-Nutzlast pro Anfrage
  • CONCURRENCY: Die Gesamtzahl der gleichzeitigen Ereignisschleifen

    Diese Zahl sollte mit der Kernanzahl der Client-VM übereinstimmen

  • RPS: Zielrate der Anfragen pro Sekunde und Ereignisschleife

  • CONNECTIONS: Anzahl der gleichzeitigen Verbindungen pro Ereignisschleife

Sehen Sie sich folgendes Beispiel an:

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

Die Ausgabe jedes Testlaufs enthält ein Histogramm der Antwortlatenzen. Im Beispiel aus der Nighthawk-Dokumentation sehen Sie, dass die Latenz des 99. Perzentils etwa 135 Mikrosekunden beträgt.

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

HTTP/2 testen

Testen Sie HTTP/2 mit folgendem Befehl:

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

Ersetzen Sie Folgendes:

  • URI: Die zu vergleichende URI
  • DURATION: Die gesamte Testlaufzeit in Sekunden
  • REQ_BODY_SIZE: Größe der POST-Nutzlast pro Anfrage
  • CONCURRENCY: Die Gesamtzahl der gleichzeitigen Ereignisschleifen

    Diese Zahl sollte mit der Kernanzahl der Client-VM übereinstimmen

  • RPS: Die Zielrate von Anfragen pro Sekunde pro Ereignisschleife

  • MAX_ACTIVE_REQUESTS: Die maximale Anzahl gleichzeitiger aktiver Anfragen für die einzelnen Ereignisschleife

  • MAX_CONCURRENT_STREAMS: die maximale Anzahl gleichzeitiger Streams, die für die einzelnen HTTP/2-Verbindung zulässig sind

Sehen Sie sich folgendes Beispiel an:

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 (Apache-Benchmark-Tool)

ab ist eine weniger flexible Alternative zu Nighthawk, ist jedoch als Paket für fast jede Linux-Distribution verfügbar. ab wird nur für schnelle und einfache Tests empfohlen.

Verwenden Sie zum Installieren von ab den folgenden Befehl:

  • Unter Debian und Ubuntu führen Sie sudo apt-get install apache2-utils aus.
  • Führen Sie bei RedHat-basierten Distributionen sudo yum install httpd-utils aus.

Führen Sie nach der Installation von ab folgenden Befehl aus, um es auszuführen:

ab -c CONCURRENCY \
    -n NUM_REQUESTS \
    -t TIMELIMIT \
    -p POST_FILE URI

Ersetzen Sie Folgendes:

  • CONCURRENCY: Anzahl der gleichzeitigen Anfragen
  • NUM_REQUESTS: Anzahl der auszuführenden Anfragen
  • TIMELIMIT: maximale Anzahl von Sekunden für Anfragen
  • POST_FILE: lokale Datei mit der HTTP-POST-Nutzlast
  • URI: Die zu vergleichende URI

Sehen Sie sich folgendes Beispiel an:

ab -c 200 -n 1000000 -t 600 -P body http://10.20.30.40:80

Der Befehl im vorherigen Beispiel sendet Anfragen mit einer Gleichzeitigkeit von 200 (Geschlossen-Schleife-Muster) und wird nach 1.000.000 (oder 1 Million) Anfragen oder 600 Sekunden verstrichener Zeit beendet. Der Befehl enthält auch den Inhalt der Datei body als HTTP-POST-Nutzlast.

Der Befehl ab erzeugt Histogramme der Antwortlatenz, die ähnlich wie Nighthawk sind, aber seine Auflösung ist auf Millisekunden anstelle von Mikrosekunden beschränkt:

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)

Nächste Schritte