6 Erkenntnisse aus Node.js Erfahrung in der Optimierung der Serviceleistung

Klarna hat große Anstrengungen unternommen, um Entwicklern dabei zu helfen, qualitativ hochwertige und sichere Dienste zu erstellen. Eines der Tools für Entwickler ist eine Plattform für die Durchführung von A / B-Tests. Die wichtigste Komponente dieses Systems ist die Vielzahl von Prozessen, die für jede eingehende Anforderung entscheiden, an welche Art von Test (A oder B) die Anforderung gesendet werden soll. Dies bestimmt wiederum, in welcher Farbe die Schaltfläche angezeigt werden soll, welches Layout dem Benutzer angezeigt werden soll oder welches Paket eines Drittanbieters verwendet werden soll. Diese Entscheidungen wirken sich direkt auf die Benutzererfahrung aus.



Klarna nutzt die Plattform Node.js. Der Artikel, dessen Übersetzung wir heute veröffentlichen, widmet sich den Lehren, die die Spezialisten des Unternehmens aus der Erfahrung bei der Optimierung der Leistung ihrer Dienstleistungen gezogen haben.

Lektion 1: Leistungstests können die Gewissheit vermitteln, dass die Geschwindigkeit des Systems nicht mit jeder Version abnimmt


Die Leistung jedes Prozesses spielt eine große Rolle, da diese Prozesse synchron in den kritischen Entscheidungspfaden des Klarna-Ökosystems verwendet werden. Die übliche Leistungsanforderung für solche Aufgaben besteht darin, dass für 99,9% der Anforderungen die Entscheidung mit einer Verzögerung getroffen werden muss, deren Zeit in einer Ziffer ausgedrückt wird. Um sicherzugehen, dass das System nicht von diesen Anforderungen abweicht, hat das Unternehmen einen Förderer zum Stresstest des Dienstes entwickelt.

Lektion Nr. 2: Unabhängiges „Aufwickeln“ der Last, Sie können Probleme erkennen, noch bevor sie die Produktion erreichen


Obwohl wir in den zwei Jahren, in denen die Plattform in der Produktion eingesetzt wurde, praktisch keine Leistungsprobleme festgestellt haben, zeigten Tests eindeutig einige Probleme. Innerhalb weniger Minuten nach dem Test stieg die Dauer der Verarbeitung der Anforderung bei einem mäßig stabilen Empfang der Anforderungen von normalen Werten auf mehrere Sekunden stark an.


Informationen zur Bearbeitungszeit der Anfrage. Es wurde ein Problem festgestellt

Wir entschieden, obwohl dies in der Produktion noch nicht geschehen war, dass es nur eine Frage der Zeit war. Wenn die tatsächliche Last ein bestimmtes Niveau erreicht, können wir auf etwas Ähnliches stoßen. Daher wurde beschlossen, dieses Problem zu untersuchen.

Lektion 3: Langzeit-Stresstests können eine Vielzahl von Problemen identifizieren. Wenn alles gut aussieht, versuchen Sie, die Testdauer zu verlängern.


Eine andere Sache, die es wert ist, beachtet zu werden, ist, dass die Probleme unseres Systems nach 2-3 Minuten Arbeit unter Last auftreten. Zuerst haben wir den Test nur 2 Minuten lang durchgeführt. Das Problem trat nur auf, wenn die Testausführungszeit auf 10 Minuten erhöht wurde.

Lektion Nr. 4: Vergessen Sie nicht, die für die DNS-Auflösung von Namen erforderliche Zeit zu berücksichtigen, wenn Sie ausgehende Anforderungen berücksichtigen. Ignorieren Sie nicht die Lebensdauer von Cache-Einträgen - dies kann die Anwendung ernsthaft stören


Normalerweise überwachen wir Dienste anhand der folgenden Metriken: Anzahl der eingehenden Anforderungen pro Sekunde, Dauer der Verarbeitung eingehender Anforderungen, Fehlerstufe. Dies gibt uns ziemlich gute Hinweise auf den Zustand des Systems und zeigt an, ob es Probleme gibt.

Diese Metriken liefern jedoch keine wertvollen Informationen während der Fehlfunktion des Dienstes. Wenn etwas schief geht, müssen Sie wissen, wo sich der Systemengpass befindet. In solchen Fällen müssen Sie die von der Node.js-Laufzeit verwendeten Ressourcen überwachen. Es ist offensichtlich, dass die Zusammensetzung der Indikatoren, deren Zustand in problematischen Situationen überwacht wird, die Verwendung des Prozessors und des Speichers umfasst. Aber manchmal hängt die Geschwindigkeit des Systems nicht von ihnen ab. In unserem Fall war beispielsweise die Prozessorauslastung niedrig. Gleiches gilt für den Speicherverbrauch.

Eine weitere Ressource, die die Leistung von Node.js-Projekten bestimmt, ist die Ereignisschleife. So wichtig es für uns ist, zu wissen, wie viel Speicher von dem Prozess verwendet wird, müssen wir auch wissen, wie viele „Tasks“ Sie zum Verarbeiten der Ereignisschleife benötigen. Die Node.js-Ereignisschleife ist in der C ++ libuv-Bibliothek implementiert ( hier ist ein gutes Video dazu). "Aufgaben" werden hier als "Aktive Anforderung" bezeichnet. Eine weitere wichtige Messgröße ist die Anzahl der "aktiven Handles", die durch offene Dateideskriptoren oder Sockets dargestellt werden, die von Node.js-Prozessen verwendet werden. Eine vollständige Liste der Deskriptortypen finden Sie in der libuv- Dokumentation . Wenn der Test 30 Verbindungen verwendet, ist zu erwarten, dass das System 30 aktive Deskriptoren hat. Der Indikator, der die Anzahl der aktiven Anforderungen kennzeichnet, gibt die Anzahl der Operationen an, die in der Warteschlange auf einen bestimmten Deskriptor warten. Was sind diese Operationen? Zum Beispiel Lese- / Schreibvorgänge. Eine vollständige Liste finden Sie hier .

Nachdem wir die Service-Metriken analysiert hatten, stellten wir fest, dass hier etwas nicht stimmte. Während die Anzahl der aktiven Deskriptoren unseren Erwartungen entsprach (in diesem Test etwa 30), war die Anzahl der aktiven Anforderungen unverhältnismäßig hoch - einige Zehntausende.


Aktive Deskriptoren und aktive Anfragen

Es stimmt, wir wussten noch nicht, welche Arten von Anfragen sich in der Warteschlange befinden. Nachdem wir die aktiven Abfragen in Typen unterteilt hatten, klärte sich die Situation ein wenig auf. UV_GETADDRINFO Abfragen haben sich nämlich als sehr auffällig erwiesen. Sie werden generiert, wenn Node.js versucht, den DNS-Namen aufzulösen.

Warum generiert das System so viele DNS-Namensauflösungsanforderungen? Es stellte sich heraus, dass der StatsD- Client versuchte, den Hostnamen für jede ausgehende Nachricht aufzulösen. Es ist zu beachten, dass dieser Client die Möglichkeit bietet, die Ergebnisse von DNS-Abfragen zwischenzuspeichern, die TTL der entsprechenden DNS-Einträge wird hier jedoch nicht berücksichtigt. Die Ergebnisse werden auf unbestimmte Zeit zwischengespeichert. Wenn der Datensatz aktualisiert wird, nachdem der Client den entsprechenden Namen bereits aufgelöst hat, wird er dies nie erfahren. Da der StatsD-Lastenausgleich mit einer anderen IP-Adresse erneut bereitgestellt werden kann und wir keinen Neustart des Dienstes erzwingen können, um den DNS-Cache zu aktualisieren, war dieser Ansatz, bei dem das zeitlich unbegrenzte Zwischenspeichern verwendet wird, für uns nicht geeignet.

Die Lösung bestand darin, ein externes Mittel für das Cachen von DNS-Abfragen zu verwenden. Dies ist einfach, indem Sie den „Affen-Patch“ des DNS-Moduls ausführen. Das Ergebnis sah jetzt viel besser aus als zuvor.


Informationen zur Bearbeitungszeit der Anfrage. Ergebnis der Verwendung eines externen DNS-Cache

Lektion 5: Ausführen von E / A-Vorgängen im Stapelmodus. Auch asynchrone Vorgänge sind ernsthafte Ressourcenverbraucher.


Nachdem wir das oben genannte Problem gelöst hatten, haben wir einige zuvor deaktivierte Funktionen des Dienstes aktiviert und erneut getestet. Insbesondere haben wir einen Code eingefügt, der für jede eingehende Anforderung eine Nachricht an das Kafka-Thema sendet. Der Test ergab erneut signifikante Peaks in den Ergebnissen von Messungen der Reaktionszeit (wir sprechen von Sekunden), die über große Zeitintervalle beobachtet wurden.


Informationen zur Bearbeitungszeit der Anfrage. Der Test ergab einen starken Anstieg der Zeit, die für die Bildung von Antworten erforderlich ist

Diese Ergebnisse weisen auf ein offensichtliches Problem genau in der Funktion hin, die wir vor dem Testen aufgenommen haben. Insbesondere sind wir mit der Tatsache konfrontiert, dass das Senden von Nachrichten an Kafka zu viel Zeit in Anspruch nimmt.


Informationen über die Zeit, die zum Generieren von Nachrichten für Kafka erforderlich ist

Wir haben uns für die einfachste Verbesserung entschieden: Ausgehende Nachrichten werden in einer Warteschlange im Speicher abgelegt und jede Sekunde im Batch-Modus gesendet. Durch erneutes Ausführen des Tests haben wir deutliche Verbesserungen in der Zeit festgestellt, die erforderlich ist, damit der Service eine Antwort bildet.


Informationen zur Bearbeitungszeit der Anfrage. Verbesserungen nach dem Organisieren der Stapelverarbeitung von Nachrichten

Lektion 6: Bereiten Sie Tests vor, denen Sie vertrauen können, bevor Sie versuchen, das System zu verbessern


Die oben beschriebene Arbeit zur Optimierung der Leistung eines Dienstes wäre ohne einen Testlaufmechanismus nicht möglich gewesen, mit dem Sie reproduzierbare und einheitliche Ergebnisse erzielen können. Die erste Version unseres Testsystems lieferte keine einheitlichen Ergebnisse, sodass wir uns bei wichtigen Entscheidungen nicht darauf verlassen konnten. Nachdem wir in die Entwicklung eines zuverlässigen Testsystems investiert hatten, konnten wir das Projekt in verschiedenen Modi testen und mit Korrekturen experimentieren. Das neue Testsystem gab uns größtenteils die Gewissheit, dass die erhaltenen Testergebnisse real waren und nicht irgendwelche Zahlen, die aus dem Nichts kamen.

Lassen Sie uns ein paar Worte über die spezifischen Tools sagen, die zum Organisieren von Tests verwendet werden.

Die Last wurde von einem internen Tool generiert, mit dem Locust problemlos im verteilten Modus ausgeführt werden konnte. Im Allgemeinen kam es auf die Ausführung eines einzelnen Befehls an, woraufhin die Lastgeneratoren gestartet, das Testskript an sie übertragen und die vom Grafana-Bedienfeld visualisierten Ergebnisse gesammelt wurden. Die entsprechenden Ergebnisse werden im Material auf Grafiken mit dunklem Hintergrund dargestellt. So sieht das System im Test aus Sicht des Kunden aus.

Der zu testende Dienst stellt Messinformationen in Datalog bereit. Diese Informationen werden hier mit Diagrammen mit hellem Hintergrund dargestellt.

Sehr geehrte Leser! Welche Node.js Service-Testsysteme verwenden Sie?

Source: https://habr.com/ru/post/de484616/


All Articles