Ursachen von App-Latenz mit Cloud Monitoring and OpenCensus identifizieren

In diesem Dokument erfahren Sie, wie Sie mit OpenCensus und Monitoring Ursachen für Extremwertlatenz erkennen und Messwerte und verteiltes Tracing für App-Entwickler verfolgen.

Wenn Sie Ihre Anwendung beobachten, stellen Sie fest, dass manche Nutzer eine höhere Anwendungslatenz haben als andere. Die Unterschiede werden noch verstärkt, wenn die Nutzerkollektive vielfältiger sind und die Ressourcennutzung steigt. Wenn Sie die Quellen hoher Latenz identifizieren und angehen, können Sie Ihre Nutzerbasis auf ein größeres Nutzerkollektiv erweitern und die Ressourcennutzung optimieren. Wenn Sie die Ressourcennutzung erhöhen, wird aufgrund der Ressourcenbeschränkungen zuerst die Extremwertlatenz deutlich.

Nutzer mit diesen höheren Latenzen sind zwar unter Umständen nicht viele, sie können aber auch wichtig sein, wenn sie beispielsweise eine wichtige Rolle in einem neuen potenziellen Markt haben.

In diesem Dokument wird erläutert, wie die Quellen hoher Latenz solcher Nutzer identifiziert und erklärt werden können. Das Dokument beantwortet insbesondere die folgenden Fragen:

  • Wie lässt sich die Extremwertlatenz genau messen?
  • Was sind die Hauptursachen für Anwendungslatenz?
  • Wie können Sie Strategien zur Minimierung der Extremwertlatenz bewerten?

Die Hauptthemen, die diese Fragen beantworten, sind:

  • Ursachen von Ungenauigkeiten in den Schätzungen von Monitoring-Messwerten erkennen
  • Ursachen für eine hohe Latenz erkennen
  • Extremwertlatenz minimieren

Der Quellcode für die Testanwendung wird in einem GitHub-Repository bereitgestellt.

In diesem Dokument wird davon ausgegangen, dass Sie mit dem grundlegenden Umgang mit OpenCensus, Cloud Monitoring, Cloud Trace, BigQuery und der Anwendungsentwicklung in Java vertraut sind.

Terminologie

Messwert
Maß für die Anwendungsleistung
Latenz
Die Zeit, die die Anwendung zum Ausführen einer Aufgabe benötigt. Die Latenzwerte in diesem Dokument werden innerhalb des Anwendungscodes gemessen.
Extremwertlatenz
Die höheren Latenzwerte, die bei einer kleinen Nutzergruppe auftreten. Die Extremwertlatenz kann auf Perzentilebene definiert werden. Das 99. Perzentil ist beispielsweise der höchste Wert von 1 % der Latenzwerte für Anfragen, die über ein Zeitintervall gemessen wurden. Weitere Informationen finden Sie im Abschnitt "Worrying about your Tail" im Kapitel "Monitoring Distributed Systems" von Site Reliability Engineering.
Aggregationsmodell
Eine Darstellung von Monitoringmesswerten, die die statistische Verteilung der erfassten Messwerte zeigt. In diesem Dokument wird von einem Histogrammmodell ausgegangen, bei dem der Latenzbereich in Buckets unterteilt ist und die Anzahl pro Bucket jeweils für ein Zeitintervall erfasst wird. Für Messwerte mit dieser Darstellung verwendet Monitoring den Begriff "Verteilungsmesswert".

Hintergrund

Inhaber von Cloud-Anwendungen beobachten häufig eine Kombination aus Median- und Extremwertlatenzwerten. Die Medianlatenz ist ein guter Indikator für den Gesamtzustand Ihrer Anwendung. Beim Vergrößern Ihrer Anwendung können jedoch auch kleinere Probleme auftreten, die aufgrund der Medianlatenz nicht einfach erkennbar sind.

Das Verständnis der Extremwertlatenz kann für mehrere Zwecke nützlich sein:

  • Früh auftretende Probleme erkennen, die in der Medianlatenz nicht erkennbar sind.
  • Gewährleisten, dass keine hohe Auslastung auf Kosten der suboptimalen Verarbeitung einiger Anfragen erreicht wird.
  • Monitoring eines Gerätepools, wenn ein einzelner Server fehlerhaft ist oder wenn ein Codepfad, dem nur eine Minderheit der Nutzer folgt, nicht gut verarbeitet wird.
  • Informative Strategien zur Minimierung der Extremwertlatenz, z. B. geeignete Werte für Zeitlimits und Backoff-Wiederholungen festlegen. Sie können die Zeitlimits auf etwas höher als die 99. Perzentillatenz einstellen, um Verbindungen abzubrechen und später wieder herzustellen, statt Anfragen für längere Zeit ausgesetzt zu lassen.

Es kann viele Ursachen für die Extremwertlatenz geben. Typische Ursachen sind:

  • Variation der Größe der Anfragenutzlast
  • Hohe CPU-Auslastung
  • Keine optimale Anwendungscodestruktur
  • Variation der Routingentfernung von Netzwerkaufrufen
  • Gelegentlicher unerwarteter Serverneustart mit verbundenen unterbrochenen Verbindungen und Kaltstarts
  • Pufferung oder Warteschlangen
  • Verbindungsverwaltung, z. B. die Wiederherstellung veralteter Verbindungen
  • Netzwerküberlastung, einschließlich Paketverlust
  • Automatische Speicherbereinigung oder Verdichtung von Daten, die zur Unterbrechung der Bearbeitung von Nutzeranfragen führen
  • Variable Latenz von Cloud-Diensten
  • Keine optimale Einstellungen für Zeitlimits bei Verbindungen

Strategien zur Ermittlung von Ursachen für Latenz:

  • Methoden zur genauen Latenzmessung erstellen: Da die Extremwertlatenz standardmäßig nur einen kleinen Teil des Traffics ausmacht, ist es wichtig, dass die Messungen genau sind.
  • Monitoring-, Tracing- und Logging-Lösungen implementieren: Mit OpenCensus und Monitoring erstellt die Anwendung ein Haupt-Span-Trace für Anfragen, die untergeordnete Spans für verschiedene Messungen enthalten. Untergeordnete Spans werden dann automatisch mit der OpenCensus-HTTP-Integration erzeugt und die Trace-Logs-Integration wird hinzugefügt, um mehr Kontext zu erfassen. Dadurch wird zusätzlicher Code für Monitoring und Tracing minimiert.
  • Daten ansehen: Sehen Sie sich Berichte und Diagramme an, in denen Sie die Extremwerte für lang dauernde Anfragen erkennen und feststellen können, welche untergeordneten Spans die längste Ausführungszeit brauchen.
  • Gründe für die Extremwertlatenz: Sammeln Sie Informationen aus ähnlichen Quellen, um die Gründe für lang dauernde untergeordnete Spans zu erkennen und sie mit Anmerkungen in Beispiel-Spans zu vergleichen.
  • Ausführlichere Abfragen verstehen: Exportieren Sie Logs nach BigQuery und kombinieren Sie sie mit Trace-Informationen, um das Anwendungsverhalten genauer zu analysieren und zu quantifizieren.

Die in diesem Dokument erläuterte Analyse ist das Ergebnis der Analyse von Systemen mit hohem Traffic, die mit Cloud Storage und Mikrodiensten skaliert wurden. Die Hauptursachen für die Variabilität der Extremwertlatenz waren Messfehler, die Größe der Nutzlast, variable Latenz in Cloud-Diensten und die Last der virtuellen Maschinen, die die Anwendung hosten.

Cloud-Nutzer haben die Wahl zwischen Messwerten aus vordefinierten Integrationen in Bibliotheken, unterstützten Messwerten von Cloud-Anbietern und benutzerdefinierten Messwerten. Beispiele für Messwerte aus vordefinierten Integrationen sind die gRPC-, HTTP- und SQL-Integrationen von OpenCensus. Google Cloud umfasst eine große Anzahl unterstützter Messwerte, die über Monitoring bereitgestellt werden. Eine weitere Alternative ist die Entwicklung von Code für zusätzliche benutzerdefinierte Messwerte, die oft Timer und Zähler sind. Der Vergleich von Zeitachsen aus diesen drei Quellen ist eine häufige Aufgabe bei der Analyse der Latenzquelle in Mikrodiensten und bei der Speicherung von Anwendungsdaten.

Übersicht über die Testanwendung

Die Testanwendung simuliert Latenzprobleme und verbindet sich mit OpenCensus und Monitoring, um die Tools und Ansätze zu zeigen, die zur Identifizierung der Probleme erforderlich sind. Der Quellcode, die Build-Dateien und die Anleitung werden im GitHub-Projekt bereitgestellt. Die Beispielanwendung ist in Java geschrieben, aber Sie können dieses Dokument auch auf andere Sprachen anwenden. Weitere Informationen zur OpenCensus-Instrumentierung von HTTP-Anfragen finden Sie in der Jetty-Integration für Java und Go. Die Testanwendung basiert auf diesem GitHub-Beispielcode.

Hier sehen Sie ein schematisches Diagramm der Anwendung.

Diagramm: Architekturdiagramm der Clientanwendung, die mit Cloud Storage und der Serveranwendung auf Compute Engine kommuniziert

Die Clientanwendung kommuniziert mit Cloud Storage und einer serverseitigen Anwendung, die auf einer Compute Engine-Instanz gehostet wird.

Hier sehen Sie einen typischen Anfragefluss:

  1. Die Cloud Storage API ruft eine JSON-Datei aus Cloud Storage in der Clientanwendung ab.
  2. Der Inhalt der Datei wird an den Server gesendet.
  3. Der Server verarbeitet einige Daten, die vom Client gesendet werden, um die Zeit und die Ressourcen einer realen Anwendung zu simulieren.
  4. Eine HTTP-Antwort wird an den Client zurückgesendet.
  5. Der Client führt eine nachgelagerte Verarbeitung durch. An dieser Stelle ersetzt die Testanwendung die einfache Arithmetik durch Geschäftslogik.

Beim Start initialisiert die Anwendung Folgendes:

  • Das Client-JUL (Java Util Logging) zum Exportieren von Cloud Logging mit Trace-IDs. Der Server verwendet ein Logback-Logging-Appender für die Trace-Log-Korrelation.
  • HTTP-Messwerte von OpenCensus sind registriert. Die HTTP-Integration für Java von OpenCensus umfasst eine Reihe von Messwertansichten für Latenz, HTTP-Antwortcode und gesendete und empfangene Bytes.
  • Der Monitoring-stats-Exporter von OpenCensus sendet zusammengefasste Messwerte in einem Hintergrundthread an Monitoring.
  • Der Monitoring-Trace-Exporter von OpenCensus sendet in einem Hintergrundthread Trace-Spans an Monitoring.
  • Die Client API von Cloud Storage lädt Dateien herunter, die für HTTP-Nutzlasten verwendet werden.
  • Ein Anwendungsthreadpool wird erstellt, um die Last für die Anwendung zu erhöhen. Mehrere Threads belasten den Server stärker als ein einzelner Thread.

Monitoring-Messwerte und Trace-Daten werden mit den folgenden Schritten erfasst und verarbeitet:

  1. OpenCensus-Bibliotheken erfassen Messwerte und Trace-Daten.
  2. OpenCensus fasst regelmäßig Messwertdaten zusammen und exportiert sie in Monitoring. Trace-Daten werden ohne Zusammenfassung in Trace exportiert.
  3. OpenCensus fügt Cloud Logging-Einträgen Trace-IDs hinzu. Logeinträge werden in den Cloud Logging-Dienst exportiert.

Sie können die mit OpenCensus erfassten Messwerte in eine Reihe von Back-End-Monitoring-Systemen exportieren, die Speicher, Analysen, Visualisierungen und Warnungen bereitstellen. Jedes System hat ein anderes Datendarstellungsformat. In diesem Dokument geht es um Monitoring.

Fehlerfreiheit von zusammengefassten Monitoring-Messwerten

OpenCensus und Monitoring bieten Tools zum Monitoring von Anwendungsleistung. Damit Sie Diagrammkurven richtig deuten, müssen Sie verstehen, wie sie erstellt wurden, insbesondere bei Kurven, die die Extremwertlatenz darstellen, z. B. das 99. Perzentil (p99). In den folgenden Abschnitten werden die Einschränkungen erläutert, die bei der Zusammenfassung von Messwertdaten zu beachten sind.

Darstellung von Messwertzeitachsen in Monitoring

Monitoring berechnet Latenzperzentile für Verteilungsmesswerte anhand von Bucket-Grenzen in numerischen Intervallen. Diese Methode wird häufig von Monitoring und OpenCensus verwendet, wobei OpenCensus Messwertdaten darstellt und in Monitoring exportiert. Die Methode TimeSeries.list für die Cloud Monitoring API gibt die Anzahl der Buckets und Grenzen für Ihr Projekt und Ihre Messwerttypen zurück. Sie können die Bucket-Grenzen im Objekt BucketOptions der Cloud Monitoring API abrufen, das Sie im API Explorer für TimeSeries.list testen können.

Verwenden Sie den folgenden Filter, um im API Explorer nach der Latenz des OpenCensus-HTTP-Clients zu filtern:

resource.type=global metric.type="custom.googleapis.com/opencensus/opencensus.io/http/client/roundtrip_latency"

Das Ergebnis enthält ein Array von Datenpunktobjekten. Das folgende Beispiel zeigt das Schema von Buckets und Bucket-Zählwerten:

"points": [ {
  "interval": { "startTime": "2019-02-14T18:11:24.971Z",
                "endTime": "2019-02-14T18:33:35.541Z" },
  "value": {
    "distributionValue": {
...
    "explicitBuckets": {
      "bounds": [0, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, ...] }
              },
      "bucketCounts": [0, 0, 0, 0, 0, 14, 17, 164, 3, 0, 1, 0, 0, 0, 0, ...]
}}]

Jeder Punkt, den Sie in einem Monitoring-Diagramm für einen Latenzwert sehen, wird aus einer Verteilung abgeleitet. Sie können die Buckets mithilfe eines Wachstumsfaktors definieren, anstatt für einzelne Buckets jeweils Grenzen zu definieren. Beispiel: Ein Wachstumsfaktor von 2,0 ergibt Bucket-Grenzen von 0–1, 1–2, 2–4, 4–8 ... Weitere Informationen zu Monitoring-Messwerten finden Sie unter Struktur von Messwerttypen.

Sie können das Diagramm so bearbeiten, dass Kurven für die 50. (Medianlatenz), 95. und 99. Perzentillatenz angezeigt werden, um das folgende Liniendiagramm aufzurufen.

Diagramm: Diagramm zur Extremwertlatenz mit dem 50., 95. und 99. Perzentil

Damit die Daten deutlich werden, die zur Berechnung dieser Perzentilwerte verwendet werden, fügen Sie den Diagrammtyp "Heatmap" hinzu. Wenn der Aggregator auf Summe festgelegt ist, wird die folgende Client-Latenz-Heatmap angezeigt.

Diagramm: Heatmap-Diagramm der Extremwertlatenz

Heatmaps sind eine Möglichkeit, die von Monitoring gespeicherten Verteilungsdaten anzuzeigen. Die Bereiche der Heatmap entsprechen den Bucket-Grenzen, die mithilfe der REST API abgerufen wurden. Sie können den Mauszeiger über die Bereiche der Heatmap halten, um herauszufinden, wie viele Punkte in einem bestimmten Bucket für einen bestimmten Zeitraum enthalten sind. Die zum Berechnen der Perzentilwerte im Liniendiagramm verwendeten Daten sind knapp.

Wenn Sie genauere Latenzmessungen wünschen, stehen Ihnen einige Strategien zur Verfügung:

  • Weitere Daten hinzufügen. Sie können mehr Daten in Tests erzeugen, aber der Traffic zu einer Produktivitätsanwendung ist unter Umständen nicht hoch genug.
  • Verkleinern Sie die Buckets und ändern Sie die Grenzen, um sie besser an Ihre Daten anzupassen. Das ist eine Option, wenn Sie eigene benutzerdefinierte Messwerte erstellen. Sie können diese Anpassungen jedoch unter Umständen nicht vornehmen, wenn Sie Daten aus vordefinierten OpenCensus-Integrationen verwenden oder von Google Cloud aus dessen Back-End-Diensten erfasst werden. Die Bucket-Grenzen für die OpenCensus-Integrationen werden von den Implementierern der OpenCensus-Integrationen in Ansichten bestimmt. Die Grenzen des HTTP-Clientlatenz-Buckets werden beispielsweise in der Klasse HttpViewConstants.java definiert. Ähnlich werden die Google Cloud-Messwerte intern in Monitoring definiert.
  • Verwenden Sie Traces oder Logs anstelle von Messwertdaten für Extremwertperzentile. Diese Optionen werden in späteren Abschnitten dieses Dokuments erläutert.

Simulierte Daten

Sie können die Genauigkeit von Aggregationsmodellen mit simulierten Rohdaten aus einer Zufallsverteilung vergleichen, um zu sehen, welchen Unterschied die Bucket-Grenzen machen können. In diesem Beispiel modelliert eine Betaverteilung die für echte Latenzwerte typische verzerrte Verteilung. Dabei handelt es sich um eine Verteilung einer kontinuierlichen Zufallsvariablen ohne Werte unter null, mit einem deutlichen Spitzenwert und einem langen abnehmenden Ende. Die Betaverteilung verwendet die Funktion random.beta aus NumPy mit den Parametern a = 2, b = 5 und wird mit einem konstanten Faktor 70 multipliziert, um realistische Latenzwerte mit einem Medianwert von 18,5 ms zu simulieren. Die Simulation enthält 100.000 zufällige Werte aus der Betaverteilung im Colab-Tabellenblatt tail_latency_lab.ipynb, das im GitHub-Projekt enthalten ist.

Das folgende Diagramm zeigt, dass Buckets mit growthFactor = 2.0 zu knappe Abstände voneinander haben, um aus ihnen korrekte Schätzungen von Extremwertlatenzen abzuleiten.

Diagramm: Zusammengefasstes Modell mit einem Wachstumsfaktor von 2,0

Die lineare Achse im Diagramm zeigt die gewünschte Form der Zufallsverteilung. Das folgende Diagramm zeigt die Bucket-Grenzen mit growthFactor = 1.4.

Diagramm: Zusammengefasstes Modell mit einem Wachstumsfaktor von 1,4

Das folgende Diagramm zeigt, dass die Bucket-Grenzen mit growthFactor = 1.2 der beabsichtigten Form ähneln, aber am oberen Ende der Verteilung noch etwas grob sind.

Diagramm: Zusammengefasstes Modell mit einem Wachstumsfaktor von 1,2

Die folgende Tabelle zeigt Schätzungen anhand der auf Buckets basierenden Verteilung im Vergleich zu einer auf Rohwerten basierenden Berechnung.

Vertrieb Median 99. Perzentil
Roh 18,5 49,5
Wachstumsfaktor 2,0 19,2 62,0
Wachstumsfaktor 1,4 18,7 53,9
Wachstumsfaktor 1,2 18,6 51,1

In diesem Fall ist ein Wachstumsfaktor von 1,2 erforderlich, um eine hinreichend korrekte Schätzung der 99. Perzentillatenz zu erhalten. Innerhalb des OpenCensus-Exporters können Sie die Werte von Rohwerten in eine Stichprobenverteilung umwandeln. Wenn Sie eine OpenCensus-Ansicht erstellen, können Sie Bucket-Grenzen konfigurieren. Ansichten enthalten ein Aggregation-Objekt. Weitere Informationen finden Sie in der Java-Dokumentation oder in der Go-Dokumentation.

Messung der HTTP-Latenz mit OpenCensus

Beim Messen der HTTP-Latenz messen Sie die Zeit, die zum Senden und Empfangen einer HTTP-Anfrage von einem Client an einen Server benötigt wird, und erfassen das Ergebnis. Sie können dies dadurch im Code implementieren, dass Sie entweder einen OpenCensus Tag erstellen oder die vordefinierte Integration für HTTP-Clients verwenden. Die HTTP-Integration wird für Go mit dem Go-HTTP-Paket und für Java mit dem Jetty-HTTP-Client bereitgestellt. Der Vorteil der OpenCensus-HTTP-Integration besteht darin, dass Sie nicht für jeden Messwert, den Sie erfassen möchten, Tag-Objekte hinzufügen müssen.

Latenzquellen identifizieren

Die zuvor beschriebene Testanwendung hat sich selbst belastet, was zu einer höheren Extremwertlatenz führte. Die folgenden Probleme werden gezeigt:

  • Gelegentlich große Nutzlasten
  • Leistungsfähige CPU
  • Kurze Zeitlimits
  • Variationen im Anwendungscodepfad mit Wiederholungen

In den folgenden Abschnitten wird beschrieben, wie diese Probleme erkannt und quantifiziert werden.

Auswirkung großer Nutzlast

Beim Untersuchen der Auswirkungen der Nutzlastgröße hat der folgende Code die Größe der gesendeten Nutzlast in 1 von 20 HTTP-Anfragen nach dem Zufallsprinzip erhöht:

static byte[] getContent(String bucket) {
  BlobId blobId = null;
  int n = rand.nextInt(100);
  if (n >= 95) {
    blobId = BlobId.of(bucket, LARGE_FILE);
  } else {
    blobId = BlobId.of(bucket, SMALL_FILE);
  }
  return storage.readAllBytes(blobId);
}

Wenn Sie die Traces für Anforderungen mit höherer Latenz in der Traceliste ansehen, können diese mit einer größeren Nutzlastgröße korreliert werden.

Grafik: Traceliste mit Anfragen mit höherer Latenz

Wenn Sie auf Ereignisse anzeigen klicken, können Sie weitere Details zur gesendeten und empfangenen Nutzlast ansehen. Bei einer hohen Latenz wird eine größere Nutzlast angezeigt.

Aus den folgenden Gründen ist Cloud Trace bei der Latenzerkennung besser als Monitoring:

  • Trace gibt Latenzwerte für Anfragen statt für zusammengefasste Werte an.
  • Trace gibt die Nutzlastgröße für die Anfrage an.

Auswirkung einer hohen CPU-Auslastung

Eine hohe CPU-Auslastung kann aufgrund der gleichzeitigen Verarbeitung zu einer höheren Extremwertlatenz führen. Dadurch sind Ressourcenbeschränkungen und eine nachfolgende Warteschlange der CPU für die Verarbeitung von Anfragen möglich. Monitoring stellt die CPU-Auslastung als integrierten Messwert für Compute Engine-Instanzen bereit. Wenn Sie jedoch Leistungstests ausführen oder Leistungsprobleme untersuchen, empfehlen wir die Installation von Monitoring-Agent. Hiermit werden mehr Betriebssysteme und Open-Source-Messwerte wie Speicher, Datenträgerverwendung und andere Messwerte abgedeckt, die verschiedene Arten von Ressourcenbeschränkungen zeigen können. Weitere Informationen finden Sie in der Liste der Agent-Messwerte.

Die Verwaltung der CPU-Auslastung ist für Serveranwendungen durch Autoscaling einfacher als für Clientanwendungen. In Compute Engine können verwaltete Instanzgruppen Autoscaling-Richtlinien anhand der durchschnittlichen CPU-Auslastung, der Load-Balancing-Servingkapazität oder Monitoring-Messwerten verwenden. Diese Richtlinien fügen virtuelle Maschinen hinzu, um den Gerätepool entsprechend zu skalieren und die Last zu bewältigen. Die konsistente Verwaltung der Client-CPU-Auslastung kann schwieriger sein und sich auf die Latenzmessung auswirken, da Nutzlasten aus dem Netzwerk gelesen und deserialisiert werden müssen, was eine CPU-Auslastung voraussetzt. Da sich die Latenzansicht der Clientanwendung stärker auf den Nutzer auswirkt, wird in diesem Dokument dieser Latenztyp behandelt.

Monitoring ist besser geeignet als Tracing, um die Auswirkungen einer hohen CPU-Auslastung zu ermitteln, da eine hohe CPU-Leistung im Allgemeinen nicht das Ergebnis einer einzigen HTTP-Anfrage ist. Im folgenden Beispiel wird die Auswirkung der CPU-Auslastung mit der Testanwendung, die mehrere Executor-Threads gleichzeitig ausführt, und dem Clientcode untersucht, der auf einer virtuellen Maschine g1-small ausgeführt wird. Anschließend werden die CPU-Auslastung und die Latenz mit den Werten der Clientanwendung verglichen, die auf einer n1-standard-1-vCPU-VM ausgeführt wird.

Die folgenden Diagramme zeigen den Medianwert und die 95. Perzentil-End-to-End-Latenz für einen Test. Die Anfragerate wurde um 3:21 Uhr dadurch erhöht, dass eine Ruheanweisung zwischen Anfragen entfernt wurde, um den Unterschied in der Latenz bei starker CPU-Auslastung anzuzeigen. Die Befehle zum Erstellen der virtuellen Maschinen und zum Ausführen der Tests sind im GitHub-Repository verfügbar.

Diagramm: Medianlatenz nach Instanz-ID-Diagramm

Das vorherige Diagramm zeigt die Medianlatenz für Client und Server während eines Lasttests. Achten Sie auf den Anstieg um 3:21 Uhr, als die Last erhöht wurde. Das folgende Diagramm zeigt die Latenz des 95. Perzentils während desselben Tests.

Diagramm: 95. Latenz nach Instanz-ID-Diagramm

Das folgende Diagramm zeigt die CPU-Auslastung für den Test. Es gibt auch einen Anstieg um 3:21 Uhr.

Diagramm: CPU-Auslastungdiagramm der Compute Engine-Instanz

Das Erhöhen der Anfragerate wirkt sich auf die Latenz aus, jedoch nicht im Verhältnis zur CPU-Auslastung. Es ist etwas überraschend, dass die Latenz sowohl für eine vCPU als auch für kleine Instanzen konsistent ist, obwohl die kleine Instanz mit etwa 78 % CPU-Auslastung ausgeführt wurde. Dieses Ergebnis deutet darauf hin, dass Sie ein Gerätepool mit einer hohen CPU-Auslastung ausführen können. Achtung: Diese Tests sind nicht als Benchmarking-Studie gedacht und Ihre Ergebnisse können während der Anwendungsimplementierung variieren.

Cloud Profiler ist ein nützliches Tool zur Untersuchung von Instanzen mit hoher CPU-Auslastung.

Latenz aufgrund von Anwendungscodezweigen und Parameterwerten

Mit Logging können Sie die schwierigsten Latenzprobleme bei Anwendungen lösen. In diesem Abschnitt wird gezeigt, wie Sie Log- und Trace-Daten kombinieren, um Latenzprobleme zu untersuchen, die von unterschiedlichen Codepfaden und Parameterwerten beeinflusst werden. Beim Trace-to-Log-Ansatz wird vermieden, dass viele Timing-Anweisungen in den Code eingefügt werden. Das häufigste Problem bei auf Mikrodiensten basierenden Anwendungen ist die Verwaltung von Zeitlimits und Wiederholungsversuchen. Lange Zeitlimits können den Nutzer stören und eine Anwendung fehlschlagen lassen, wenn sie zu viele Verbindungen herstellen. Wir empfehlen, ein kurzes Zeitlimit festzulegen, eine Weile zu warten und es dann noch einmal zu versuchen. Aggressive Wiederholungsversuche können jedoch zu erneuten Belastungen führen und die Wiederherstellung von Vorfällen erschweren. Sie sollten das Zeitlimit so einstellen, dass nur bei einem kleinen Prozentsatz der Anfragen, z. B. bei weniger als 1 %, Wiederholungen erforderlich sind. Die Testanwendung legt jedoch die Wiederholungszeit für HTTP-Anfragen an den Testserver auf einen niedrigen Wert fest, um Wiederholungsversuche in Aktion darzustellen, ohne auf einen tatsächlichen Ausfall warten zu müssen.

Wiederholungsversuche werden auch in der Google API-Clientbibliothek für Cloud Storage implementiert, sodass Sie diese nicht in Ihrem eigenen Code implementieren müssen. Weitere Informationen zum exponentiellen Backoff finden Sie unter Abgeschnittener exponentieller Backoff.

Mit der Integration von OpenCensus Monitoring-Logkorrelationen können Sie die Logeinträge aus Traces ansehen. Bei dieser Integration wird ein Enhancer von Cloud Logging verwendet, um Trace-Informationen zu Logs hinzuzufügen. Sie können ihn dadurch zu Ihrer Anwendung hinzufügen, dass Sie Logging für Java einrichten und die Maven-Abhängigkeit opencensus-contrib-log-correlation-stackdriver1 zu Ihrem Build hinzufügen. Bei der Erstellung dieses Dokuments ist die Logintegration mit Go nicht implementiert, aber Sie können das Feature anfordern. Die aktuelle Alternative für Go besteht darin, Trace-Spans Anmerkungen mit den erforderlichen Informationen hinzuzufügen.

Die Methode in der Testanwendung zum Vorbereiten, Senden und Verarbeiten von Anfragen an den Mikrodienst lautet so:

private void prepareSendProcess(
    HttpClient httpClient,
    HttpMethod method,
    Function<Integer[], Integer> downStreamFn,
    String fnName)
    throws InterruptedException {
  Tracer tracer = Tracing.getTracer();
  try (Scope scope = tracer.spanBuilder("main").startScopedSpan()) {
    StopWatch s = StopWatch.createStarted();
    byte[] content = new byte[0];
    if (method == HttpMethod.POST) {
      content = TestInstrumentation.getContent(testOptions.bucket());
    }
    byte[] payload = sendWithRetry(httpClient, method, content);
    TestInstrumentation.processPayload(payload, downStreamFn, fnName);
    TestInstrumentation.recordTaggedStat(
        method.toString(), s.getTime(TimeUnit.NANOSECONDS) / 1.0e6);
  }
}

Die Methode erstellt einen begrenzten Span als Trace auf oberster Ebene für die Anfrage, die den Code für alle Vorbereitungsphasen, das Senden der HTTP-Anfrage an den Server und die nachgelagerte Verarbeitung abdeckt. Die Methode prepareSendProcess ruft sendWithRetry auf, das die HTTP-Anfrage mit einem Wiederholungsmechanismus umschließt:

private byte[] sendWithRetry(HttpClient httpClient, HttpMethod method, byte[] content)
    throws InterruptedException {
  ExponentialBackOff backoff = new ExponentialBackOff.Builder()
    .setInitialIntervalMillis(500)
    .setMaxElapsedTimeMillis(5*60*1000)
    .setMultiplier(2.0)
    .setRandomizationFactor(0.5)
    .build();
  for (int i = 0; i < MAX_RETRIES; i++) {
    try {
      return sendRequest(httpClient, method, content);
    } catch (RetryableException e) {
      LOGGER.log(Level.WARNING, "RetryableException attempt: " + (i + 1) + " " + e.getMessage());
    } catch (InterruptedException e) {
      LOGGER.log(
          Level.WARNING, "InterruptedException attempt: " + (i + 1) + " " + e.getMessage());
    } catch (TimeoutException e) {
      LOGGER.log(Level.WARNING, "TimeoutException attempt: " + (i + 1) + " " + e.getMessage());
    } catch (ExecutionException e) {
      LOGGER.log(Level.WARNING, "ExecutionException attempt: " + (i + 1) + " " + e.getMessage());
    }
    try {
      Thread.sleep(backoff.nextBackOffMillis());
    } catch(IOException e) {
      throw new RuntimeException("MaxElapsedTime exceeded");
    }
  }
  throw new RuntimeException("Max retries exceeded");
}

Die folgende Methode sendet die HTTP-Anfrage:

private byte[] sendRequest(HttpClient httpClient, HttpMethod method, byte[] content)
    throws InterruptedException, TimeoutException, ExecutionException, RetryableException {
  String targetURL = testOptions.targetURL();
  HttpRequest request = (HttpRequest) httpClient.newRequest(targetURL).method(method);
  if (request == null) {
    throw new RetryableException("Request is null");
  }
  if (method == HttpMethod.POST) {
    ContentProvider contentProvider =
        new StringContentProvider(new String(content, StandardCharsets.UTF_8));
    request.content(contentProvider, "application/json");
  }
  request.timeout(testOptions.httpTimeout(), TimeUnit.MILLISECONDS);
  ContentResponse response = request.send();
  int status = response.getStatus();
  LOGGER.info("Response status: " + status + ", " + method);
  if (HttpStatus.isSuccess(status)) {
    byte[] payload = response.getContent();
    LOGGER.info("Response payload: " + payload.length + " bytes");
    return payload;
  } else if (HttpStatus.isServerError(status)) {
    throw new RetryableException(response.getReason());
  }
  return new byte[0];
}

Diese Methode erstellt ein HTTPRequest-Objekt und fügt dann die Nutzlast hinzu, wenn eine POST-Anfrage gesendet wird. Im Folgenden wird ein Beispiel-Trace beschrieben.

Beispiel-Trace für ein

Wenn Sie in Monitoring auf Logs ansehen klicken, werden die Logs im Trace-Detail angezeigt. Wenn Sie auf das Symbol Trace-Detail klicken, wie unter Traces suchen und ansehen beschrieben, werden die Trace-Informationen angezeigt. Sie können den Logtext mit dem Quellcode abgleichen, um festzustellen, welcher Codepfad zum Erstellen des Trace verwendet wurde, sowie Details anderer Parameter wie HTTP-Methode und -Antwort ansehen. Das vorherige Beispiel zeigt, dass in diesem Trace eine Zeitüberschreitung aufgetreten ist. Der Span ist anschließend lang, da er vom Backoff bestraft wird. Außerdem gibt es sechs Wiederholungsversuche, was die maximal konfigurierte Anzahl ist.

Klicken Sie auf Ereignisse anzeigen, um Trace-Ereignisse anzusehen.

Trace-Ereignisse mit hoher Latenz

Die hohe Latenz wurde durch eine große Nutzlast verursacht. Das Zeitlimit von 8 ms war zu niedrig, um diese große Nutzlast bewältigen zu können. Es sollte auf einen höheren Wert zurückgesetzt werden.

Den Trace-Spans können Sie Anmerkungen mit ähnlichen Informationen wie bei Logberichten hinzufügen. Wenn Sie ihnen jedoch wichtige Informationen zu den verschiedenen Codepfaden in Ihrer Anwendung hinzufügen möchten, müssen Sie Code für Trace-Spans in den Anwendungscode einfügen. Wenn Ihr Entwicklerteam mit Tracing nicht vertraut ist, ist es unter Umständen einfacher, diese Informationen in Logberichten mit JUL oder Logback hinzuzufügen und Logging für Java einzurichten. Außerdem enthält Ihr Code eventuell bereits Logberichte.

Zur Erkennung der langsamsten Anfragen können Sie einen Trace-Analysebericht erstellen, der Beispiel-Traces in verschiedenen Anfrageperzentilen enthält, wie im folgenden Screenshot gezeigt.

Tabelle: Trace-Analysebericht mit Beispiel-Traces in verschiedenen Anfrageperzentilen

Sie können auf die Beispiel-Traces klicken, um detaillierte Traces für die höheren Perzentile anzusehen.

Logberichte und Trace-IDs in BigQuery kombinieren

Für weitere Loginformationen können Sie die Logs direkt in der Cloud Logging-Loganzeige mit einem Logfilter abfragen.

Logabfrage mit Logfilter

Ein Vorteil dieses Filters ist, dass er drei Logeinträge mit derselben Trace-ID verbindet. Sie haben die Möglichkeit, Logeinträge aus derselben Anfrage mit Trace-IDs zu verbinden. Anschließend können Sie die Logeinträge mit einem Filter wie resource.type="gce_instance" in BigQuery exportieren.

Grafik: Logfilter für die Compute Engine-Instanz

Wenn Sie die Logs in ein Dataset mit dem Namen application_test_dataset exportieren, können Sie die Logs mit der folgenden Abfrage untersuchen:

SELECT
  TIME(timestamp) AS Time,
  SUBSTR(textPayload, 90) as Message,
  SUBSTR(trace, 31) as trace_id
FROM
  `application_test_dataset.java_log_20190328`
WHERE REGEXP_CONTAINS(textPayload, "attempt")
LIMIT 10;
...
Time             Message                            trace_id
10:59:11.782000  WARNING: TimeoutException attempt: 008a0ce...

Eine Zeile mit Beispieldaten wird angezeigt. Das Ergebnis dieser Abfrage liefert ähnliche Informationen wie die Loganzeige. Da sich die Logdaten jedoch jetzt in BigQuery befinden, können Sie leistungsstärkere Abfragen ausführen. Die folgende Abfrage zeigt, wie viele Anfragen eine unterschiedliche Anzahl von Wiederholungen hatten:

SELECT
  RetryCount,
  COUNT(trace) AS RequestCount
FROM(
  SELECT
    COUNT(REGEXP_CONTAINS(textPayload, "attempt")) as RetryCount,
    trace
  FROM
    `application_test_dataset.java_log_20190328`
  WHERE NOT REGEXP_CONTAINS(trace, '00000000000000000000000000000000')
  Group BY trace
)
GROUP BY RetryCount
ORDER BY RetryCount DESC
LIMIT 10;
...
RetryCount RequestCount
8          13
7          180
6          2332
5          242
4          507
3          416605
1          1

In den Abfrageergebnissen sehen Sie, dass es 13 Anfragen mit maximal acht Wiederholungen gab. Sie können die Ergebnisse dieser Abfrage verwenden, um Ihre Wiederholungsparameter zu verfeinern. Sie haben beispielsweise die Möglichkeit, das Zeitlimit zu erhöhen, das Zeitlimit mit der Nutzlastgröße zu variieren oder die maximale Anzahl an Wiederholungen zu erhöhen.

Weitere Informationen