Heim > Web-Frontend > js-Tutorial > Hauptteil

Verwenden von Node.js mit Nginx zur Implementierung von network_node.js mit hoher Last

WBOY
Freigeben: 2016-05-16 15:52:32
Original
1259 Leute haben es durchsucht

Wenn es um die Erstellung von Webanwendungen mit hohem Durchsatz geht, sind NginX und Node.js eine natürliche Ergänzung. Sie basieren alle auf dem ereignisgesteuerten Modell und können den C10K-Engpass herkömmlicher Webserver wie Apache problemlos überwinden. Mit der Standardkonfiguration kann bereits eine hohe Parallelität erreicht werden, aber wenn Sie auf günstiger Hardware mehr als Tausende von Anfragen pro Sekunde erreichen möchten, gibt es noch einiges zu tun.

In diesem Artikel wird davon ausgegangen, dass Leser das HttpProxyModule von NginX verwenden, um als Reverse-Proxy für den Upstream-Server node.js zu fungieren. Wir werden die Optimierung von sysctl in Systemen mit Ubuntu 10.04 und höher sowie die Optimierung von node.js-Anwendungen und NginX vorstellen. Wenn Sie ein Debian-System verwenden, können Sie das gleiche Ziel natürlich auch erreichen, die Optimierungsmethoden sind jedoch unterschiedlich.

Netzwerkoptimierung

Wenn Sie nicht zunächst die zugrunde liegenden Übertragungsmechanismen von Nginx und Node.js verstehen und eine gezielte Optimierung durchführen, ist die Optimierung möglicherweise vergeblich, egal wie detailliert die beiden sind. Im Allgemeinen verbindet Nginx den Client und die Upstream-Anwendungen über TCP-Sockets.

Unser System verfügt über viele Schwellenwerte und Einschränkungen für TCP, die über Kernel-Parameter festgelegt werden. Die Standardwerte dieser Parameter werden häufig für allgemeine Zwecke festgelegt und können den Anforderungen von Webservern an hohen Datenverkehr und kurze Lebensdauer nicht gerecht werden.


Hier sind einige Parameter, die für die Optimierung von TCP in Frage kommen. Um sie wirksam zu machen, können Sie sie in die Datei /etc/sysctl.conf oder in eine neue Konfigurationsdatei wie /etc/sysctl.d/99-tuning.conf einfügen und dann sysctl -p ausführen Lassen Sie den Kernel sie laden. Wir verwenden sysctl-cookbook, um diese körperliche Arbeit zu erledigen.

Es ist zu beachten, dass die hier aufgeführten Werte sicher zu verwenden sind. Es wird jedoch dennoch empfohlen, dass Sie die Bedeutung jedes Parameters studieren, um einen geeigneteren Wert basierend auf Ihrer Last, Hardware und Nutzung auszuwählen.

Code kopieren Der Code lautet wie folgt:
net.ipv4.ip_local_port_range='1024 65000'
net.ipv4.tcp_tw_reuse='1'
net.ipv4.tcp_fin_timeout='15'
net.core.netdev_max_backlog='4096'
net.core.rmem_max='16777216'
net.core.somaxconn='4096'
net.core.wmem_max='16777216'
net.ipv4.tcp_max_syn_backlog='20480'
net.ipv4.tcp_max_tw_buckets='400000'
net.ipv4.tcp_no_metrics_save='1'
net.ipv4.tcp_rmem='4096 87380 16777216'
net.ipv4.tcp_syn_retries='2'
net.ipv4.tcp_synack_retries='2'
net.ipv4.tcp_wmem='4096 65536 16777216'
vm.min_free_kbytes='65536'

Heben Sie einige wichtige hervor.

Code kopieren Der Code lautet wie folgt:
net.ipv4.ip_local_port_range

Um den Downstream-Client für die Upstream-Anwendung zu bedienen, muss NginX zwei TCP-Verbindungen öffnen, eine zum Client und eine zur Anwendung. Wenn ein Server viele Verbindungen empfängt, sind die verfügbaren Ports des Systems schnell erschöpft. Durch Ändern des Parameters net.ipv4.ip_local_port_range können Sie den Bereich der verfügbaren Ports vergrößern. Wenn in /var/log/syslog ein solcher Fehler gefunden wird: „Mögliches SYN-Flooding auf Port 80. Cookies werden gesendet“, bedeutet dies, dass das System keinen verfügbaren Port finden kann. Durch Erhöhen des Parameters net.ipv4.ip_local_port_range kann dieser Fehler reduziert werden.

Code kopieren Der Code lautet wie folgt:
net.ipv4.tcp_tw_reuse

Wenn der Server zwischen einer großen Anzahl von TCP-Verbindungen wechseln muss, wird eine große Anzahl von Verbindungen im Status TIME_WAIT generiert. TIME_WAIT bedeutet, dass die Verbindung selbst geschlossen ist, die Ressourcen jedoch nicht freigegeben wurden. Wenn Sie net_ipv4_tcp_tw_reuse auf 1 setzen, kann der Kernel versuchen, Verbindungen wiederzuverwenden, wenn dies sicher ist. Dies ist viel kostengünstiger als die Wiederherstellung neuer Verbindungen.

Code kopieren Der Code lautet wie folgt:
net.ipv4.tcp_fin_timeout

Dies ist die Mindestzeit, die eine Verbindung im Status TIME_WAIT warten muss, bevor sie wiederhergestellt wird. Eine Verkleinerung kann das Recycling beschleunigen.
So überprüfen Sie den Verbindungsstatus

Verwenden Sie netstat:

Code kopieren Der Code lautet wie folgt:
netstat -tan | sortieren |. uniq -c

oder verwenden Sie ss:

Code kopieren Der Code lautet wie folgt:
ss -s

NginX

Wenn die Belastung des Webservers allmählich zunimmt, werden wir auf einige seltsame Einschränkungen von NginX stoßen. Die Verbindung wird unterbrochen und der Kernel meldet weiterhin SYN-Flut. Zu diesem Zeitpunkt sind der Lastdurchschnitt und die CPU-Auslastung sehr gering, und der Server kann offensichtlich mehr Verbindungen verarbeiten, was wirklich frustrierend ist.

Nach einer Untersuchung wurde festgestellt, dass sich viele Verbindungen im Status TIME_WAIT befinden. Dies ist die Ausgabe von einem der Server:

Code kopieren Der Code lautet wie folgt:
ss -s
Gesamt: 388 (Kernel 541)
TCP: 47461 (estab 311, geschlossen 47135, verwaist 4, synrecv 0, Wartezeit 47135/0), Ports 33938

Gesamt-IP IPv6 transportieren
* 541 - - -
RAW 0 0 0 0
UDP 13 10 3
TCP 326 325 1
INET 339 335 4
FRAG 0 0 0 0


Es gibt 47135 TIME_WAIT-Verbindungen! Darüber hinaus ist aus ss ersichtlich, dass es sich bei allen um geschlossene Verbindungen handelt. Dies weist darauf hin, dass der Server die meisten verfügbaren Ports verbraucht hat, und impliziert auch, dass der Server jeder Verbindung neue Ports zuweist. Das Optimieren des Netzwerks half ein wenig bei der Lösung des Problems, aber es waren immer noch nicht genügend Ports vorhanden.

Nach weiteren Recherchen habe ich ein Dokument über den Uplink-Keepalive-Befehl gefunden, das lautet:

  • Legen Sie die maximale Anzahl inaktiver Keep-Alive-Verbindungen zum Upstream-Server fest. Diese Verbindungen werden im Cache des Arbeitsprozesses beibehalten.

Interessant. Theoretisch minimiert dieses Setup die Verschwendung von Verbindungen, indem Anfragen über zwischengespeicherte Verbindungen weitergeleitet werden. In der Dokumentation wird auch erwähnt, dass wir „proxy_http_version“ auf „1.1“ setzen und den „Connection“-Header löschen sollten. Nach weiteren Recherchen habe ich herausgefunden, dass dies eine gute Idee ist, da HTTP/1.1 die Nutzung von TCP-Verbindungen im Vergleich zu HTTP1.0 erheblich optimiert und Nginx standardmäßig HTTP/1.0 verwendet.

Nach der Änderung wie im Dokument vorgeschlagen sieht unsere Uplink-Konfigurationsdatei wie folgt aus:

Code kopieren Der Code lautet wie folgt:
upstream backend_nodejs {
server nodejs-3:5016 max_fails=0 fail_timeout=10s;
server nodejs-4:5016 max_fails=0 fail_timeout=10s;
server nodejs-5:5016 max_fails=0 fail_timeout=10s;
server nodejs-6:5016 max_fails=0 fail_timeout=10s;
keepalive 512;
}

Ich habe auch die Proxy-Einstellungen im Serverbereich wie vorgeschlagen geändert. Gleichzeitig wurde ein Proxy_next_upstream hinzugefügt, um ausgefallene Server zu überspringen, das Keepalive_Timeout des Clients angepasst und das Zugriffsprotokoll deaktiviert. Die Konfiguration sieht folgendermaßen aus:

Code kopieren Der Code lautet wie folgt:
Server {
Hör zu 80;
Servername fast.gosquared.com;

client_max_body_size 16M;
keepalive_timeout 10;

Standort / {
Proxy_next_upstream-Fehler-Timeout http_500 http_502 http_503 http_504;
Proxy_set_header-Verbindung „“;
​ Proxy_http_version 1.1;
Proxy_pass http://backend_nodejs;
}

access_log off;
error_log /dev/null crit;
}

Nach der Übernahme der neuen Konfiguration stellte ich fest, dass die von den Servern belegten Sockets um 90 % reduziert wurden. Anfragen können jetzt über deutlich weniger Verbindungen übermittelt werden. Die neue Ausgabe lautet wie folgt:

Code kopieren Der Code lautet wie folgt:
ss -s

Gesamt: 558 (Kernel 604)
TCP: 4675 (estab 485, geschlossen 4183, verwaist 0, synrecv 0, Wartezeit 4183/0), Ports 2768

Gesamt-IP IPv6 transportieren
* 604 - - -
RAW 0 0 0 0
UDP 13 10 3
TCP 492 491 1
INET 505 501 4

Node.js

Dank des ereignisgesteuerten Designs, das I/O asynchron verarbeitet, kann Node.js eine große Anzahl von Verbindungen und Anfragen sofort verarbeiten. Obwohl es andere Optimierungsmethoden gibt, konzentriert sich dieser Artikel hauptsächlich auf den Prozessaspekt von node.js.

Der Knoten ist Single-Threaded und verwendet nicht automatisch mehrere Kerne. Mit anderen Worten: Die Anwendung kann nicht automatisch alle Funktionen des Servers nutzen.

Erzielen Sie ein Clustering von Knotenprozessen

Wir können die Anwendung so ändern, dass sie mehrere Threads aufteilt und Daten auf demselben Port empfängt, wodurch die Last über mehrere Kerne verteilt werden kann. Node verfügt über ein Cluster-Modul, das alle zum Erreichen dieses Ziels erforderlichen Tools bereitstellt. Das Hinzufügen dieser Tools zur Anwendung erfordert jedoch viel manuelle Arbeit. Wenn Sie Express verwenden, verfügt eBay über ein Modul namens cluster2, das verwendet werden kann.

Kontextwechsel verhindern

Wenn Sie mehrere Prozesse ausführen, sollten Sie sicherstellen, dass jeder CPU-Kern jeweils nur mit einem Prozess beschäftigt ist. Wenn die CPU über N Kerne verfügt, sollten wir im Allgemeinen N-1 Anwendungsprozesse generieren. Dadurch wird sichergestellt, dass jeder Prozess eine angemessene Zeitspanne erhält, sodass ein Kern frei bleibt, damit der Kernel-Scheduler andere Aufgaben ausführen kann. Wir müssen außerdem sicherstellen, dass grundsätzlich keine anderen Aufgaben außer Node.js auf dem Server ausgeführt werden, um CPU-Konflikte zu verhindern.

Wir haben einmal einen Fehler gemacht und zwei node.js-Anwendungen auf dem Server bereitgestellt, und dann hat jede Anwendung N-1 Prozesse geöffnet. Dadurch konkurrieren sie untereinander um die CPU, wodurch die Systemlast stark ansteigt. Obwohl unsere Server alle 8-Core-Maschinen sind, ist der durch den Kontextwechsel verursachte Leistungsaufwand immer noch deutlich zu spüren. Unter Kontextwechsel versteht man das Phänomen, dass die CPU die aktuelle Aufgabe unterbricht, um andere Aufgaben auszuführen. Beim Umschalten muss der Kernel den gesamten Status des aktuellen Prozesses anhalten und dann einen anderen Prozess laden und ausführen. Um dieses Problem zu lösen, haben wir die Anzahl der von jeder Anwendung gestarteten Prozesse reduziert, damit sie sich die CPU gerecht teilen können. Dadurch wurde die Systemlast reduziert:

2015628112206774.png (802×404)

Bitte achten Sie auf das Bild oben, um zu sehen, wie die Systemlast (blaue Linie) unter die Anzahl der CPU-Kerne (rote Linie) sinkt. Auf anderen Servern haben wir dasselbe gesehen. Da die Gesamtarbeitslast gleich bleibt, kann die Leistungsverbesserung in der obigen Grafik nur auf die Reduzierung der Kontextwechsel zurückgeführt werden.

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!