Hier sind 10 Leistungsregeln, die wir bei der Verwendung von Node.js befolgen:
1. Vermeiden Sie die Verwendung von synchronem Code
Vom Design her ist Node.js Single-Threaded. Damit ein einzelner Thread viele gleichzeitige Anforderungen verarbeiten kann, darf ein Thread niemals auf blockierende, synchrone oder lang laufende Vorgänge warten. Eine Besonderheit von Node.js besteht darin, dass es von oben nach unten entworfen und implementiert wird, um eine asynchrone Implementierung zu erreichen. Dies macht es ideal für veranstaltungsbasierte Programme.
Leider kann es weiterhin zu synchronen/blockierenden Aufrufen kommen. Beispielsweise gibt es für viele Dateisystemvorgänge sowohl synchrone als auch asynchrone Versionen, wie etwa writeFile und writeFileSync. Selbst wenn Sie Code zur Steuerung der Synchronisationsmethode verwenden, ist es immer noch möglich, versehentlich eine externe Funktionsbibliothek zu verwenden, die Aufrufe blockiert. Wenn Sie dies tun, ist die Auswirkung auf die Leistung enorm.
// Good: write files asynchronously fs.writeFile('message.txt', 'Hello Node', function (err) { console.log("It's saved and the server remains responsive!"); }); // BAD: write files synchronously fs.writeFileSync('message.txt', 'Hello Node'); console.log("It's saved, but you just blocked ALL requests!");
Unsere Implementierung des Initialisierungsprotokolls enthielt versehentlich einen synchronen Aufruf zum Schreiben des Inhalts auf die Festplatte. Wenn wir keine Leistungstests durchführen, kann dieses Problem leicht ignoriert werden. Beim Test mit einer node.js-Instanz in der Entwicklerbox führt dieser synchrone Aufruf dazu, dass die Leistung von Tausenden von Anfragen pro Sekunde auf nur einige Dutzend sinkt.
2. Schließen Sie den Socket-Pool
Der Node.js-HTTP-Client verwendet automatisch Socket-Pooling: Standardmäßig ist jeder Host auf 5 Sockets beschränkt. Während die Wiederverwendung von Sockets den Ressourcenanstieg möglicherweise unter Kontrolle hält, kann sie zu einer Reihe von Engpässen führen, wenn Sie viele gleichzeitige Datenanforderungen vom selben Host verarbeiten müssen. In diesem Fall ist es eine gute Idee, den Wert von maxSockets zu erhöhen oder den Socket-Pool auszuschalten:
// Disable socket pooling var http = require('http'); var options = {.....}; options.agent = false; var req = http.request(options)
3. Lassen Sie nicht zu, dass statische Ressourcen Node.js verwenden
Verwenden Sie für statische Ressourcen wie CSS und Bilder den Standard-WebServer anstelle von Node.js. Beispielsweise verwendet LinkedIn Mobile Nginx. Wir nutzen auch Content Delivery Networks (CDNs), die statische Assets auf Server auf der ganzen Welt kopieren können. Dies hat zwei Vorteile: (1) Es kann die Belastung unseres node.js-Servers reduzieren. (2) CDNs können die Wartezeit verkürzen, indem sie die Bereitstellung statischer Inhalte auf Servern ermöglichen, die näher an den Benutzern liegen.
4. Auf dem Client rendern
Vergleichen wir kurz die Unterschiede zwischen Server-Rendering und Client-Rendering. Wenn wir node.js zum Rendern auf der Serverseite verwenden, senden wir für jede Anfrage eine HTML-Seite wie die folgende zurück:
<!-- An example of a simple webpage rendered entirely server side --> <!DOCTYPE html> <html> <head> <title>LinkedIn Mobile</title> </head> <body> <div class="header"> <img src="http://mobile-cdn.linkedin.com/images/linkedin.png" alt="LinkedIn"/> </div> <div class="body"> Hello John! </div> </body> </html>
Bitte beachten Sie, dass es sich bei allen Inhalten auf dieser Seite, mit Ausnahme des Benutzernamens, um statische Inhalte handelt: Der Inhalt ist für jeden Benutzer und jedes erneute Laden der Seite derselbe. Daher ist es effizienter, Node.js nur den von der Seite benötigten dynamischen Inhalt in JSON-Form zurückgeben zu lassen.
{"name": "John"}
Der Rest der Seite – alles statisches HTML-Markup – kann in einer JavaScript-Vorlage (z. B. der Vorlage underscore.js) platziert werden:
<!-- An example of a JavaScript template that can be rendered client side --> <!DOCTYPE html> <html> <head> <title>LinkedIn Mobile</title> </head> <body> <div class="header"> <img src="http://mobile-cdn.linkedin.com/images/linkedin.png" alt="LinkedIn"/> </div> <div class="body"> Hello <%= name %>! </div> </body> </html>
Die Leistungsverbesserung kommt von diesen Stellen: Wie im dritten Punkt erwähnt, können statische JavaScript-Vorlagen serverseitig über den Webserver (z. B. Nginx) bereitgestellt oder über ein besseres CDN implementiert werden. Darüber hinaus können JavaScript-Vorlagen im Browser zwischengespeichert oder lokal gespeichert werden. Nach dem ersten Laden der Seite müssen nur JSON-Daten an den Client gesendet werden, was am effektivsten ist. Diese Methode kann die CPU-, IO- und Node.js-Last erheblich reduzieren.
5. Verwenden Sie gzip
Viele Server und Clients unterstützen gzip, um Anfragen und Antworten zu komprimieren. Ganz gleich, ob Sie Clients beantworten oder Anfragen an Remote-Server senden: Nutzen Sie das volle Potenzial aus.
6. Parallelisierung
Versuchen Sie, alle Ihre Blockierungsvorgänge zu parallelisieren – Anfragen an Remote-Dienste, DB-Aufrufe, Dateisystemzugriffe. Dadurch wird die Wartezeit des langsamsten Blockierungsvorgangs und nicht die Wartezeit aller Blockierungsvorgänge verkürzt. Um Rückrufe und Fehlerbehandlung sauber zu halten, verwenden wir Step, um den Ablauf zu steuern.
7.Sitzungsliberalisierung
LinkedIn Mobile verwendet das Express-Framework, um den Anfrage-/Antwortzyklus zu verwalten. Viele Express-Beispiele umfassen die folgende Konfiguration:
app.use(express.session({ Secret: "keyboard cat" }));
Standardmäßig werden Sitzungsdaten im Speicher gespeichert, was den Server enorm belastet, insbesondere wenn die Anzahl der Benutzer steigt. Sie können einen externen Sitzungsspeicher wie MongoDB oder Redis verwenden, aber jede Anfrage verursacht den Overhead eines Remote-Aufrufs zum Abrufen der Sitzungsdaten. Wenn möglich, ist es am besten, alle zustandslosen Daten auf der Serverseite zu speichern. Wenn Sie die Sitzung freigeben, indem Sie die oben genannte Express-Konfiguration nicht einbeziehen, erzielen Sie eine bessere Leistung.
8. Verwendung von Binärmodulen
Wenn möglich, ersetzen Sie JavaScript-Module durch Binärmodule. Wenn wir beispielsweise von einem in JavaScript geschriebenen SHA-Modul zu einer kompilierten Version für Node.js wechseln, sehen wir einen enormen Leistungssprung:
// Use built in or binary modules var crypto = require('crypto'); var hash = crypto.createHmac("sha1",key).update(signatureBase).digest("base64");
9. Ersetzen Sie die Client-Bibliothek durch Standard-V8-JavaScript
Viele JavaScript-Bibliotheken werden für die Verwendung in Webbrowsern erstellt, weil sich die JavaScript-Umgebung unterscheidet: Einige Browser unterstützen beispielsweise Funktionen wie forEach, Map und Reduce, andere jedoch nicht. Daher verwenden Clientbibliotheken oft viel ineffizienten Code, um Browserunterschiede zu überwinden. In Node.js hingegen wissen Sie genau, welche JavaScript-Methoden gültig sind: Die V8-JavaScript-Engine unterstützt die ECMAScript-Implementierung von Node.js, wie in ECMA-262 5th Edition angegeben. Ersetzen Sie einfach die Client-Bibliothek durch standardmäßige V8-JavaScript-Funktionen und Sie werden erhebliche Leistungsverbesserungen feststellen.
10. Halten Sie Ihren Code klein und leicht
Die Verwendung mobiler Geräte kann zu langsamem Zugriff und hoher Latenz führen, was uns dazu zwingt, unseren Code klein und leicht zu halten. Behalten Sie die gleiche Philosophie auch für den Servercode bei. Schauen Sie gelegentlich auf Ihre Entscheidungen zurück und stellen Sie sich Fragen wie: „Brauchen wir dieses Modul wirklich?“, „Warum verwenden wir dieses Framework und lohnt sich der Mehraufwand?“, „Können wir es auf einfachere Weise erreichen?“ " Kleinerer und leichterer Code ist normalerweise effizienter und schneller.
Probieren Sie es aus
Wir arbeiten hart daran, unsere mobilen Apps schnell zu machen. Probieren Sie es mit der iPhone-App, der Android-App und der mobilen HTML5-Version aus und teilen Sie uns mit, wie es Ihnen geht.