Ich habe angefangen, an Node.js HTTP-Server zu basteln und schreibe wirklich gerne serverseitiges Javascript, aber etwas hindert mich daran, Node.js für meine Webanwendung zu verwenden.
Ich verstehe das gesamte asynchrone E / A-Konzept, bin jedoch etwas besorgt über die Randfälle, in denen prozeduraler Code sehr CPU-intensiv ist, z. B. Bildmanipulation oder Sortieren großer Datenmengen.
Soweit ich weiß, ist der Server für einfache Webseitenanforderungen wie das Anzeigen einer Benutzerliste oder das Anzeigen eines Blogposts sehr schnell. Wenn ich jedoch sehr CPU-intensiven Code schreiben möchte (z. B. im Admin-Backend), der Grafiken generiert oder die Größe von Tausenden von Bildern ändert, ist die Anforderung sehr langsam (einige Sekunden). Da dieser Code nicht asynchron ist, werden alle Anforderungen, die während dieser wenigen Sekunden an den Server gesendet werden, blockiert, bis meine langsame Anforderung abgeschlossen ist.
Ein Vorschlag war, Web Worker für CPU-intensive Aufgaben zu verwenden. Ich befürchte jedoch, dass Web-Worker das Schreiben von sauberem Code erschweren werden, da dies durch das Einfügen einer separaten JS-Datei funktioniert. Was ist, wenn sich der CPU-intensive Code in der Methode eines Objekts befindet? Es ist schade, eine JS-Datei für jede Methode zu schreiben, die CPU-intensiv ist.
Ein weiterer Vorschlag war, einen untergeordneten Prozess zu erzeugen, aber das macht den Code noch weniger wartbar.
Irgendwelche Vorschläge, um dieses (wahrgenommene) Hindernis zu überwinden? Wie schreibt man mit Node.js sauberen objektorientierten Code, während sichergestellt wird, dass CPU-schwere Aufgaben asynchron ausgeführt werden?
quelle
Antworten:
Was Sie brauchen, ist eine Aufgabenwarteschlange! Es ist eine gute Sache, Ihre lang laufenden Aufgaben vom Webserver zu entfernen. Wenn Sie jede Aufgabe in einer "separaten" js-Datei aufbewahren, wird die Modularität und die Wiederverwendung von Code gefördert. Es zwingt Sie, darüber nachzudenken, wie Sie Ihr Programm so strukturieren können, dass es auf lange Sicht einfacher zu debuggen und zu warten ist. Ein weiterer Vorteil einer Aufgabenwarteschlange besteht darin, dass die Mitarbeiter in einer anderen Sprache geschrieben werden können. Stellen Sie einfach eine Aufgabe auf, erledigen Sie die Arbeit und schreiben Sie die Antwort zurück.
so etwas https://github.com/resque/resque
Hier ist ein Artikel von Github darüber, warum sie es gebaut haben http://github.com/blog/542-introducing-resque
quelle
Dies ist ein Missverständnis der Definition von Webserver - es sollte nur verwendet werden, um mit Clients zu "sprechen". Schwerlastaufgaben sollten an eigenständige Programme delegiert werden (dies kann natürlich auch in JS geschrieben werden).
Sie würden wahrscheinlich sagen, dass es schmutzig ist, aber ich versichere Ihnen, dass ein Webserverprozess, der beim Ändern der Bildgröße festsitzt, nur schlechter ist (selbst für Apache, wenn er andere Abfragen nicht blockiert). Sie können jedoch eine gemeinsame Bibliothek verwenden, um Code-Redundanz zu vermeiden.
EDIT: Ich habe mir eine Analogie ausgedacht; Webanwendung sollte als Restaurant sein. Sie haben Kellner (Webserver) und Köche (Arbeiter). Die Kellner stehen mit den Kunden in Kontakt und erledigen einfache Aufgaben wie das Bereitstellen eines Menüs oder das Erklären, ob ein Gericht vegetarisch ist. Zum anderen delegieren sie härtere Aufgaben an die Küche. Da die Kellner nur einfache Dinge tun, reagieren sie schnell und die Köche können sich auf ihre Arbeit konzentrieren.
Node.js hier wäre ein einzelner, aber sehr talentierter Kellner, der viele Anfragen gleichzeitig bearbeiten kann, und Apache wäre eine Gruppe dummer Kellner, die jeweils nur eine Anfrage bearbeiten. Wenn dieser Kellner von Node.j anfangen würde zu kochen, wäre dies eine unmittelbare Katastrophe. Dennoch könnte das Kochen auch einen großen Vorrat an Apache-Kellnern erschöpfen, ganz zu schweigen vom Chaos in der Küche und der fortschreitenden Abnahme der Reaktionsfähigkeit.
quelle
Sie möchten nicht, dass Ihr CPU-intensiver Code asynchron ausgeführt wird, sondern dass er parallel ausgeführt wird . Sie müssen die Verarbeitungsarbeit aus dem Thread herausholen, der HTTP-Anforderungen bedient. Nur so kann dieses Problem gelöst werden. Bei NodeJS lautet die Antwort das Cluster-Modul, zum Laichen von Kinderprozessen, um das schwere Heben zu erledigen. (AFAIK Node hat kein Konzept für Threads / Shared Memory; es sind Prozesse oder nichts). Sie haben zwei Möglichkeiten, wie Sie Ihre Anwendung strukturieren. Sie können die 80/20-Lösung erhalten, indem Sie 8 HTTP-Server erzeugen und rechenintensive Aufgaben synchron für die untergeordneten Prozesse ausführen. Das zu tun ist ziemlich einfach. Es kann eine Stunde dauern, bis Sie unter diesem Link darüber gelesen haben. Wenn Sie nur den Beispielcode oben auf diesem Link abreißen, erhalten Sie 95% des Weges dorthin.
Die andere Möglichkeit, dies zu strukturieren, besteht darin, eine Jobwarteschlange einzurichten und große Rechenaufgaben über die Warteschlange zu senden. Beachten Sie, dass mit dem IPC für eine Jobwarteschlange viel Overhead verbunden ist. Dies ist daher nur dann nützlich, wenn die Aufgaben erheblich größer sind als der Overhead.
Ich bin überrascht , dass keiner dieser anderen Antworten auch erwähnen Cluster.
Hintergrund: Asynchroner Code ist Code, der angehalten wird, bis irgendwo anders etwas passiert . An diesem Punkt wird der Code aktiviert und die Ausführung fortgesetzt. Ein sehr häufiger Fall, in dem irgendwo anders etwas Langsames passieren muss, ist E / A.
Asynchroner Code ist nicht nützlich, wenn Ihr Prozessor für die Arbeit verantwortlich ist. Genau das ist bei "rechenintensiven" Aufgaben der Fall.
Nun scheint es, dass asynchroner Code eine Nische ist, aber tatsächlich ist er sehr verbreitet. Es ist einfach nicht nützlich für rechenintensive Aufgaben.
Das Warten auf E / A ist ein Muster, das beispielsweise auf Webservern immer auftritt. Jeder Client, der eine Verbindung zu Ihrem Server herstellt, erhält einen Socket. Meistens sind die Steckdosen leer. Sie möchten nichts tun, bis ein Socket einige Daten empfängt. An diesem Punkt möchten Sie die Anforderung bearbeiten. Unter der Haube verwendet ein HTTP-Server wie Node eine Eventing-Bibliothek (libev), um die Tausenden offener Sockets zu verfolgen. Das Betriebssystem benachrichtigt libev, und libev benachrichtigt NodeJS, wenn einer der Sockets Daten abruft. Anschließend stellt NodeJS ein Ereignis in die Ereigniswarteschlange, und Ihr http-Code wird an dieser Stelle aktiviert und behandelt die Ereignisse nacheinander. Ereignisse werden erst in die Warteschlange gestellt, wenn der Socket einige Daten enthält, sodass Ereignisse niemals auf Daten warten - sie sind bereits für sie da.
Ereignisbasierte Webserver mit einem Thread sind als Paradigma sinnvoll, wenn der Engpass auf eine Reihe von meist leeren Socket-Verbindungen wartet und Sie nicht für jede inaktive Verbindung einen ganzen Thread oder Prozess benötigen und Ihre 250.000 nicht abfragen möchten Sockets, um den nächsten zu finden, der Daten enthält.
quelle
Einige Ansätze, die Sie verwenden können.
Wie @Tim feststellt, können Sie eine asynchrone Aufgabe erstellen, die sich außerhalb oder parallel zu Ihrer Hauptversorgungslogik befindet. Hängt von Ihren genauen Anforderungen ab, aber auch Cron kann als Warteschlangenmechanismus fungieren.
WebWorker können für Ihre asynchronen Prozesse arbeiten, werden jedoch derzeit von node.js nicht unterstützt. Es gibt einige Erweiterungen, die Unterstützung bieten, z. B. http://github.com/cramforce/node-worker
Sie erhalten weiterhin die Möglichkeit, Module und Code über den Standardmechanismus "Erforderlich" wiederzuverwenden. Sie müssen nur sicherstellen, dass beim ersten Versand an den Mitarbeiter alle Informationen übergeben werden, die zur Verarbeitung der Ergebnisse erforderlich sind.
quelle
Verwendung
child_process
ist eine Lösung. Aber jeder untergeordnete Prozess, der erzeugt wird, kann im Vergleich zu Go viel Speicher verbrauchengoroutines
Sie können auch eine warteschlangenbasierte Lösung wie kue verwenden
quelle