Wir kennen Node.js für seine blitzschnelle Performance. Doch wie bei jeder Sprache kann man auch bei Node.js Code schreiben, der für die Nutzer schlechter läuft, als man es gerne hätte. Um dies zu bekämpfen, brauchen wir angemessene Leistungstests. Heute werden wir genau das mit einem detaillierten Blick auf das Einrichten und Ausführen eines Leistungstests und das Analysieren der Ergebnisse abdecken, damit Sie blitzschnelle Node.js-Anwendungen erstellen können.
Der beste Weg, um zu verstehen, wie Sie Ihre Anwendung einem Leistungstest unterziehen, ist, ein Beispiel durchzugehen.
Sie können Node.js für viele Zwecke verwenden: das Schreiben von Skripten zum Ausführen von Aufgaben, das Ausführen eines Webservers oder das Bereitstellen von statischen Dateien, wie zum Beispiel einer Website. Heute werden wir die Schritte zum Testen einer Node.js-HTTP-Web-API durchgehen. Aber wenn Sie etwas anderes in Node bauen, machen Sie sich keine Sorgen – viele der Prinzipien werden ähnlich sein.
- Die Einzigartigkeit der Node.js-Performance
- Die Ereignisschleife
- Node.js-Leistung und die Ereignisschleife
- Schritt 1: Auswahl von Node.js-Leistungstest-Tools
- Schritt 2: Erstellen eines Node.js-Leistungstestprofils
- Nutzung mehrerer Testprofile
- Replizieren großer verteilter Systeme
- Schritt 3: Richten Sie Ihre Beobachtbarkeit/Überwachung ein
- Mit einem APM zum Laufen bringen
- Auf Node.js zugeschnittene Tools
- Schritt 4: Erstellen der Infrastruktur für Node.js-Leistungstests
- Schritt 5: Führen Sie Ihre Tests aus!
- Jetzt haben Sie blitzschnelles Node.js
Die Einzigartigkeit der Node.js-Performance
Bevor wir beginnen, lassen Sie uns einen kurzen Blick auf eine der einzigartigen Eigenschaften der Node.js-Performance werfen. Wir müssen diese Eigenschaften kennen, wenn wir später unsere Leistungstests durchführen.
Wovon spreche ich?
Die wichtigste Überlegung bei Node.js-Anwendungen ist ihr Single-Thread-Verhalten, das durch die sogenannte Ereignisschleife unterstützt wird. Ich weiß, was Sie jetzt denken: Das ist eine Menge. Also lassen Sie uns das ein wenig aufschlüsseln, damit wir verstehen, was das bedeutet.
Beginnen wir mit Single-Threading. Threading, als Konzept, ermöglicht die gleichzeitige Verarbeitung innerhalb einer Anwendung. Node.js hat diese Fähigkeit nicht, zumindest nicht im traditionellen Sinne. Um stattdessen Anwendungen zu schreiben, die mehrere Aufgaben gleichzeitig ausführen, haben wir den asynchronen Code und die Ereignisschleife.
Die Ereignisschleife
Was ist die Ereignisschleife?
Die Ereignisschleife ist Node.js‘ Art, lang laufende Prozesse in kleine Abschnitte zu unterteilen. Sie funktioniert wie ein Herzschlag: Alle paar Millisekunden prüft Node.js eine Arbeitswarteschlange, um neue Aufgaben zu starten. Wenn es Arbeit gibt, bringt es diese auf den Aufrufstapel und führt sie dann bis zum Abschluss aus (wir werden bald über Run-to-Completion sprechen).
Durch das Aufteilen von Aufgaben kann Node.js Multitasking betreiben, was ein Ersatz für Threading ist. Das bedeutet, dass, während eine Aufgabe wartet, eine andere starten kann. Anstelle von Threading verwenden wir also asynchronen Code, der durch Programmierstile wie Callbacks, Promises und async/await unterstützt wird. Die meisten Node-APIs haben sowohl eine synchrone als auch eine asynchrone Ausführungsmethode.
Okay, vielleicht fragst du dich jetzt: Was hat dieser ganze Fachjargon mit Leistung zu tun?
Lassen Sie es mich erklären…
Node.js-Leistung und die Ereignisschleife
Stellen Sie sich vor, Sie bauen eine Node.js-Anwendung mit zwei Endpunkten: einen für das Hochladen von Dateien und einen, der ein Benutzerprofil abruft. Die Benutzerprofil-API wird wahrscheinlich wesentlich häufiger angefordert als der Datei-Upload, und wenn sie nicht schnell genug antwortet, blockiert sie jeden Seitenladevorgang für jeden Benutzer – das ist nicht gut.
Die Benutzer-Upload-API wird nur selten verwendet. Außerdem erwarten die Benutzer, dass das Hochladen von Aufgaben Zeit in Anspruch nimmt, aber sie sind viel weniger nachsichtig mit Seitenladezeiten. Wenn wir nicht mit der Ereignisschleife im Hinterkopf programmieren, während die Datei hochgeladen wird, könnte Node.js am Ende alle Systemressourcen in Anspruch nehmen und andere Benutzer von der Nutzung Ihrer Anwendung abhalten – oh weh!
Und deshalb müssen Sie die Single-Thread-Natur von Node.js verstehen. Wenn wir unsere Anwendung ändern, müssen wir dieses Verhalten berücksichtigen. Wir wollen es vermeiden, lang laufende Aufgaben synchron auszuführen, wie z. B. Netzwerkanfragen, das Schreiben von Dateien oder das Ausführen umfangreicher Berechnungen.
Nun, da wir die Single-Thread-Natur von Node.js kennen, können wir sie zu unserem Vorteil nutzen. Gehen wir Schritt für Schritt vor, wie Sie einen Leistungstest Ihrer Node.js-Anwendung einrichten, ausführen und analysieren können, um sicherzustellen, dass Sie das Beste aus den Leistungsfähigkeiten von Node.js herausholen.
Schritt 1: Auswahl von Node.js-Leistungstest-Tools
Zuerst müssen Sie ein Tool auswählen, mit dem Sie Ihre Leistungstests durchführen können. Es gibt viele Tools, alle mit unterschiedlichen Vor- und Nachteilen für das Node.js-Performance-Tuning. Ein wichtiger Punkt ist, dass, obwohl Sie eine Node.js-Anwendung testen, wenn Sie Leistungstests von außen über ein Netzwerk durchführen, es keine Rolle spielt, ob Ihr Leistungstest-Tool in Node.js geschrieben ist.
Für grundlegende HTTP-Leistungstests mag ich Artillery, ein einfaches, in Node.js geschriebenes Leistungstest-Tool. Es eignet sich auch besonders gut für die Durchführung von Leistungstests für API-Anfragen. Artillery funktioniert durch das Schreiben einer Konfigurationsdatei, die Ihr Lastprofil definiert. Sie teilen Artillery mit, welche Endpunkte Sie anfragen möchten, mit welcher Rate, für welche Dauer usw.
Ein grundlegendes Testskript sieht so aus:
config: target: 'https://artillery.io' phases: - duration: 60 arrivalRate: 20 defaults: headers: x-my-service-auth: '987401838271002188298567'scenarios:- flow:- get:url: "/docs"
Hier fordern Sie die Website von Artillery für eine Dauer von 60 Sekunden an, wobei 20 neue Benutzer an der URL ankommen.
Um den Test auszuführen, führen Sie einfach aus:
artillery run your_config.yml
Artillery wird so viele Anfragen an Ihre Anwendung stellen, wie Sie ihm aufgetragen haben. Dies ist nützlich, um Leistungstestprofile zu erstellen, die Ihre Produktionsumgebung imitieren. Was meine ich mit Leistungstestprofil?
Schritt 2: Erstellen eines Node.js-Leistungstestprofils
Ein Leistungstestprofil ist, wie oben beschrieben, eine Definition, wie Ihr Leistungstest ablaufen wird. Sie sollten möglichst nachahmen, wie sich Ihr Produktionsverkehr verhält oder wie es erwartet wird, um eine genaue Node.js-Leistungsoptimierung durchzuführen. Wenn Sie z. B. eine Veranstaltungs-Website erstellen, erwarten Sie viel Verkehr um die Zeit, in der Sie Tickets freigeben, also sollten Sie ein Profil erstellen, das dieses Verhalten nachahmt. Sie möchten testen, ob Ihre Anwendung in der Lage ist, in kurzer Zeit große Mengen an Last zu bewältigen. Wenn Sie eine E-Commerce-Website betreiben, erwarten Sie vielleicht einen gleichmäßigen Datenverkehr. In diesem Fall sollten Ihre Leistungstestprofile dieses Verhalten widerspiegeln.
Nutzung mehrerer Testprofile
Ein interessanter Punkt ist, dass Sie verschiedene Testprofile erstellen und diese überlappend ausführen können. So können Sie beispielsweise ein Profil erstellen, das Ihren Basis-Traffic simuliert (z. B. 100 Anfragen pro Minute), und dann simulieren, was passieren könnte, wenn Sie einen hohen Traffic auf Ihrer Website verzeichnen, z. B. wenn Sie Suchmaschinenwerbung schalten. Das Testen mehrerer Szenarien ist wichtig für ein gründliches Node.js-Performance-Tuning.
Replizieren großer verteilter Systeme
Ich muss hier kurz etwas anmerken: Wenn eine Anwendung eine bestimmte Größe erreicht, verliert die Nachahmung der Last auf diese Weise an Durchführbarkeit. Der Verkehr, den Sie haben, könnte so wild, unvorhersehbar oder groß sein, dass es schwierig ist, eine realistische Möglichkeit zu schaffen, Ihre Anwendung vor der Veröffentlichung zu testen.
Aber was, wenn das der Fall ist? Was tun wir dann? Wir testen in der Produktion.
Sie denken jetzt vielleicht: „Moment mal, woah! Sollten wir nicht vor der Freigabe testen?“
Das können Sie, aber wenn ein System eine bestimmte Größe erreicht, kann es sinnvoll sein, andere Leistungsteststrategien zu nutzen. Sie können Konzepte wie das Canary-Release nutzen, um Ihre Änderungen in die Produktion einzubringen und sie nur mit einem bestimmten Prozentsatz der Benutzer zu testen. Wenn Sie einen Leistungsabfall feststellen, können Sie den Datenverkehr wieder auf die vorherige Implementierung umstellen. Dieser Prozess fördert das Experimentieren, und das Beste daran ist, dass Sie mit Ihrer echten Produktionsanwendung testen, so dass Sie sich keine Sorgen darüber machen müssen, dass die Testergebnisse nicht mit der Produktion übereinstimmen.
So weit haben wir uns für unser Tooling entschieden und Profile erstellt, die unsere Produktion nachbilden, z. B. Datenverkehr und Arbeitslasten. Was machen wir als nächstes? Wir müssen sicherstellen, dass wir die Daten haben, die wir für die Analyse unserer Anwendung benötigen, und das tun wir mit Node.js-Performance-Monitoring- und Application Performance Management (APM)-Tools. Was ist ein APM? Lesen Sie weiter, und ich erkläre es Ihnen!
Schritt 3: Richten Sie Ihre Beobachtbarkeit/Überwachung ein
Wir wollen unseren Leistungstest nicht einfach gegen unsere Anwendung laufen lassen und hoffen und beten. Wenn wir das tun, werden wir nicht in der Lage sein, zu verstehen, wie die Anwendung funktioniert und ob sie das tut, was sie unserer Meinung nach tun sollte. Bevor wir also beginnen, sollten wir uns Fragen stellen wie „Wie sieht eine gute Leistung für meine Anwendung aus? Was sind meine SLAs und KPIs? Welche Metriken werden benötigt, um ein Leistungsproblem effektiv zu beheben?“
Wenn Ihre Anwendung langsam oder anders als erwartet funktioniert, benötigen Sie Daten, um zu verstehen, warum das so ist, damit Sie sie verbessern können. Alle produktiven Anwendungen, die etwas taugen, verwenden irgendeine Form der Beobachtbarkeit und/oder Überwachungslösung. Diese Tools, die oft APMs genannt werden, ermöglichen es Ihnen, kritische Node.js-Performance-Metriken über Ihre laufende Anwendung einzusehen.
Mit einem APM zum Laufen bringen
APMs gibt es in verschiedenen Formen und Größen, alle mit unterschiedlichen Funktionen, Preisschildern, Sicherheitsimplikationen, Performance, was auch immer. Es lohnt sich, sich ein wenig umzuschauen, um das beste Tool für Ihre Bedürfnisse zu finden. Es sind diese Tools, die uns die Einblicke und Daten liefern, die wir brauchen, wenn wir unsere Node.js-Performance-Tests durchführen.
Wenn wir also wissen, dass wir unsere Anwendung überwachen sollten – was genau sollten wir überwachen?
Im Grunde wollen Sie so viele Daten wie möglich – aber so sehr wir Daten auch lieben, wir müssen realistisch sein, wo wir anfangen! Am besten fängt man mit den folgenden drei Bereichen an:
- Aggregierte Protokolle Anwendungsprotokolle werden entweder implizit von einigen Bibliotheken oder explizit von einem Entwickler ausgegeben, um einen Einblick in eine Anwendung zu erhalten. Die meisten Tools für aggregierte Protokolle ermöglichen eine einfache Suche und Visualisierung der protokollierten Daten. In unserem Fall konnten wir die Leistung jeder unserer APIs protokollieren und in einem Diagramm darstellen.
- Einblicke in die Infrastruktur – Ihre Anwendung wird auf verschiedenen Hosts ausgeführt, sodass Sie wahrscheinlich alle Daten sehen möchten. Wenn Sie in der Cloud arbeiten, stellen Ihnen die meisten Anbieter diese Daten (wenn auch in grober Form) sofort zur Verfügung. Die Daten, die Sie von diesen Tools erhalten, decken Dinge wie die CPU- und Speichernutzung Ihres Hosts, Verbindungsdaten usw. ab.
- Anwendungsüberwachung – Diese Art von Tool sitzt in der Regel in Ihrem Anwendungscode und kann Erkenntnisse darüber liefern, wie Funktionen ausgeführt/aufgerufen werden, welche Fehler wir auslösen usw.
Einige APM-Tools wie Retrace bieten alle oder die meisten dieser drei Funktionen in einem Tool, während andere spezialisierter sein können. Je nach Ihren Anforderungen möchten Sie vielleicht ein Tool, das alles kann, oder eine ganze Reihe von Tools für verschiedene Zwecke.
Auf Node.js zugeschnittene Tools
Zusätzlich zu den Tools können wir auch andere Node.js-spezifische Tools und Profiler, wie Flame-Graphen, einbeziehen, die unsere Funktionsausführung betrachten oder Daten über die Ausführung unserer Ereignisschleife extrahieren. Je mehr Sie sich mit Node.js-Performance-Tests vertraut machen, desto größer werden Ihre Anforderungen an die Daten. Sie sollten sich ständig umsehen, experimentieren und Ihre Werkzeuge aktualisieren, um Ihre Anwendung wirklich zu verstehen.
Nachdem wir unsere Werkzeuge eingerichtet, realistische Profile für unsere Leistung erstellt und die Leistung unserer Anwendung verstanden haben, sind wir fast bereit, unsere Tests durchzuführen. Aber bevor wir das tun, gibt es noch einen weiteren Schritt: die Erstellung der Testinfrastruktur.
Schritt 4: Erstellen der Infrastruktur für Node.js-Leistungstests
Sie können Leistungstests von Ihrem eigenen Rechner aus durchführen, wenn Sie das möchten, aber es gibt Probleme, wenn Sie das tun. Bisher haben wir uns wirklich Mühe gegeben – zum Beispiel mit unseren Testprofilen – um sicherzustellen, dass unsere Leistungstests repliziert werden. Ein weiterer Faktor bei der Replikation unserer Tests ist, dass wir sie immer auf derselben Infrastruktur (sprich: Maschine) ausführen.
Eine der einfachsten Möglichkeiten, eine konsistente Testinfrastruktur zu schaffen, ist die Nutzung von Cloud-Hosting. Wählen Sie einen Host/eine Maschine, von dem/der aus Sie Ihre Tests starten möchten, und stellen Sie sicher, dass Sie Ihre Tests immer von derselben Maschine aus starten – und vorzugsweise auch vom selben Standort aus -, um zu vermeiden, dass Ihre Daten aufgrund von Anfragelatenz verzerrt werden.
Es ist eine gute Idee, diese Infrastruktur zu skripten, damit Sie sie nach Bedarf erstellen und abbauen können. Man nennt diese Idee „Infrastruktur als Code“. Die meisten Cloud-Anbieter unterstützen dies von Haus aus, oder Sie können ein Tool wie Terraform verwenden, um Ihnen dabei zu helfen.
Puh! Wir haben bis jetzt eine Menge Boden abgedeckt und sind nun beim letzten Schritt angelangt: der Ausführung unserer Tests.
Schritt 5: Führen Sie Ihre Tests aus!
Der letzte Schritt ist die tatsächliche Ausführung unserer Tests. Wenn wir unsere Kommandozeilenkonfiguration starten (wie in Schritt 1), sehen wir Anfragen an unsere Node.js-Anwendung. Mit unserer Überwachungslösung können wir überprüfen, wie sich unsere Ereignisschleife verhält, ob bestimmte Anfragen länger dauern als andere, ob Verbindungen zeitlich begrenzt sind usw.
Das Tüpfelchen auf dem i für Ihre Leistungstests ist, sie in Ihre Build- und Testpipeline zu integrieren. Eine Möglichkeit, dies zu tun, besteht darin, die Leistungstests über Nacht laufen zu lassen, so dass Sie sie jeden Morgen überprüfen können. Artillery bietet eine schöne, einfache Möglichkeit, diese Berichte zu erstellen, die Ihnen dabei helfen können, jegliche Node.js-Leistungsrückschritte zu erkennen.
Jetzt haben Sie blitzschnelles Node.js
Das war’s.
Heute haben wir uns mit der Bedeutung der Ereignisschleife für die Leistung Ihrer JavaScript-Anwendung befasst, mit der Auswahl von Werkzeugen für Leistungstests, mit der Einrichtung von konsistenten Leistungstestprofilen mit Artillery, mit der Überwachung, die Sie zur Diagnose von Node.js-Performance-Probleme zu diagnostizieren, und schließlich, wie und wann Sie Ihre Performance-Tests ausführen, um den größtmöglichen Nutzen für Sie und Ihr Team zu erzielen.
Experimentieren Sie mit Monitoring-Tools wie Retrace APM für Node.js, nehmen Sie kleine Änderungen vor, um die Auswirkungen der Änderungen zu testen, und überprüfen Sie Ihre Testberichte häufig, um Regressionen zu erkennen. Jetzt haben Sie alles, was Sie brauchen, um die Leistungsfähigkeiten von Node.js zu nutzen und eine superperformante Anwendung zu schreiben, die Ihre Benutzer lieben!