Ich glaube, die Erlang-Community ist nicht neidisch auf Node.js, da sie nicht blockierende E / A nativ ausführt und Möglichkeiten bietet, Bereitstellungen einfach auf mehr als einen Prozessor zu skalieren (etwas, das nicht einmal in Node.js integriert ist). Weitere Informationen finden Sie unter http://journal.dedasys.com/2010/04/29/erlang-vs-node-js und Node.js oder Erlang
Was ist mit Haskell? Kann Haskell einige der Vorteile von Node.js bieten, nämlich eine saubere Lösung, um das Blockieren von E / A zu vermeiden, ohne auf Multithread-Programmierung zurückgreifen zu müssen?
Es gibt viele Dinge, die mit Node.js attraktiv sind
- Ereignisse: Keine Thread-Manipulation, der Programmierer bietet nur Rückrufe an (wie im Snap-Framework).
- Rückrufe werden garantiert in einem einzigen Thread ausgeführt: Keine Racebedingung möglich.
- Schöne und einfache UNIX-freundliche API. Bonus: Hervorragende HTTP-Unterstützung. DNS ebenfalls verfügbar.
- Jede E / A ist standardmäßig asynchron. Dies erleichtert das Vermeiden von Sperren. Zu viel CPU-Verarbeitung in einem Rückruf wirkt sich jedoch auf andere Verbindungen aus (in diesem Fall sollte die Aufgabe in kleinere Unteraufgaben aufgeteilt und neu geplant werden).
- Gleiche Sprache für clientseitig und serverseitig. (Ich sehe jedoch nicht zu viel Wert in diesem. JQuery und Node.js teilen sich das Ereignisprogrammierungsmodell, aber der Rest ist sehr unterschiedlich. Ich kann nur nicht sehen, wie das Teilen von Code zwischen Server- und Client-Seite möglich ist in der Praxis nützlich sein.)
- All dies in einem einzigen Produkt verpackt.
Antworten:
Ok, nachdem ich ein wenig von der Präsentation von node.js gesehen habe , auf die @gawi mich hingewiesen hat, kann ich etwas mehr darüber sagen, wie Haskell mit node.js verglichen wird. In der Präsentation beschreibt Ryan einige der Vorteile von Green Threads, sagt dann aber, dass er das Fehlen einer Thread-Abstraktion nicht als Nachteil empfindet. Ich würde seiner Position nicht zustimmen, insbesondere im Zusammenhang mit Haskell: Ich denke, die Abstraktionen, die Threads bereitstellen, sind wesentlich, damit Servercode leichter richtig und robuster wird. Bestimmtes:
Wenn Sie einen Thread pro Verbindung verwenden, können Sie Code schreiben, der die Kommunikation mit einem einzelnen Client ausdrückt, anstatt Code zu schreiben, der alle Clients gleichzeitig behandelt. Stellen Sie sich das so vor: Ein Server, der mehrere Clients mit Threads verarbeitet, sieht fast genauso aus wie einer, der einen einzelnen Client verarbeitet. Der Hauptunterschied ist, dass es
fork
irgendwo im ersteren einen gibt. Wenn das von Ihnen implementierte Protokoll überhaupt komplex ist, wird die Verwaltung der Zustandsmaschine für mehrere Clients gleichzeitig ziemlich schwierig, während Sie in Threads nur die Kommunikation mit einem einzelnen Client per Skript ausführen können. Der Code ist leichter richtig zu machen und leichter zu verstehen und zu pflegen.Rückrufe auf einem einzelnen Betriebssystem-Thread sind kooperatives Multitasking im Gegensatz zu präemptivem Multitasking, das Sie mit Threads erhalten. Der Hauptnachteil beim kooperativen Multitasking besteht darin, dass der Programmierer dafür verantwortlich ist, dass kein Hunger auftritt. Es verliert an Modularität: Machen Sie einen Fehler an einer Stelle und es kann das gesamte System vermasseln. Dies ist wirklich etwas, worüber Sie sich keine Sorgen machen müssen, und Preemption ist die einfache Lösung. Darüber hinaus ist eine Kommunikation zwischen Rückrufen nicht möglich (dies würde zum Stillstand führen).
Parallelität ist in Haskell nicht schwer, da der meiste Code rein und daher von Natur aus threadsicher ist. Es gibt einfache Kommunikationsprimitive. In Haskell ist es viel schwieriger, sich mit Parallelität in den Fuß zu schießen, als in einer Sprache mit uneingeschränkten Nebenwirkungen.
quelle
Ja, tatsächlich sind Ereignisse und Threads in Haskell vereinheitlicht.
Threads werden tatsächlich in Bezug auf Ereignisse implementiert und über mehrere Kerne mit nahtloser Thread-Migration, dokumentierter Leistung und Anwendungen ausgeführt.
ZB für
Gleichzeitige Sammlungen nbody auf 32 Kernen
In Haskell haben Sie sowohl Ereignisse als auch Themen, und wie es alle Ereignisse unter der Haube sind.
Lesen Sie das Dokument, in dem die Implementierung beschrieben wird.
quelle
Erstens bin ich nicht der Ansicht, dass node.js das Richtige tut, um all diese Rückrufe aufzudecken. Am Ende schreiben Sie Ihr Programm in CPS (Continuation Passing Style), und ich denke, es sollte die Aufgabe des Compilers sein, diese Transformation durchzuführen.
In diesem Sinne können Sie auf Wunsch mit einem asynchronen Stil schreiben. Auf diese Weise würden Sie jedoch das Schreiben in einem effizienten synchronen Stil mit einem Thread pro Anforderung verpassen. Haskell ist lächerlich effizient im synchronen Code, insbesondere im Vergleich zu anderen Sprachen. Es sind alles Ereignisse darunter.
Sie könnten immer noch eine Race-Bedingung in node.js haben, aber es ist schwieriger.
Jede Anfrage ist in einem eigenen Thread. Wenn Sie Code schreiben, der mit anderen Threads kommunizieren muss, ist es dank der Parallelitätsprimitiven von haskell sehr einfach, ihn threadsicher zu machen.
Werfen Sie einen Blick in Hackage und überzeugen Sie sich selbst.
Sie haben keine derartigen Probleme, ghc wird Ihre Arbeit auf echte Betriebssystem-Threads verteilen.
Haskell kann hier unmöglich gewinnen ... richtig? Denken Sie noch einmal darüber nach, http://www.haskell.org/haskellwiki/Haskell_in_web_browser .
Laden Sie ghc herunter, starten Sie die Kabale. Für jeden Bedarf gibt es ein Paket.
quelle
Ich persönlich sehe Node.js und das Programmieren mit Rückrufen als unnötig niedrig und etwas unnatürlich an. Warum mit Rückrufen programmieren, wenn eine gute Laufzeit wie die in GHC Rückrufe für Sie verarbeiten kann und dies ziemlich effizient?
In der Zwischenzeit hat sich die GHC-Laufzeit erheblich verbessert: Es gibt jetzt einen "neuen neuen E / A-Manager" namens MIO, wobei "M" für Multicore steht, glaube ich. Es baut auf der Grundlage des vorhandenen E / A-Managers auf und hat das Hauptziel, die Ursache für Leistungseinbußen bei mehr als 4 Kernen zu überwinden. Die in diesem Dokument angegebenen Leistungszahlen sind ziemlich beeindruckend. Sehen Sie selbst:
Und:
Mio hat es in die GHC 7.8.1-Version geschafft. Ich persönlich sehe dies als einen großen Fortschritt in der Leistung von Haskell. Es wäre sehr interessant, die Leistung bestehender Webanwendungen zu vergleichen, die mit der vorherigen GHC-Version und 7.8.1 kompiliert wurden.
quelle
IMHO-Ereignisse sind gut, die Programmierung mittels Rückrufen jedoch nicht.
Die meisten Probleme, die das Codieren und Debuggen von Webanwendungen besonders machen, sind darauf zurückzuführen, dass sie skalierbar und flexibel sind. Das wichtigste ist die Staatenlosigkeit von HTTP. Dies verbessert die Navigationsfähigkeit, führt jedoch zu einer Umkehrung der Steuerung, wenn das E / A-Element (in diesem Fall der Webserver) verschiedene Handler im Anwendungscode aufruft. Dieses Ereignismodell - oder genauer gesagt das Rückrufmodell - ist ein Albtraum, da Rückrufe keine variablen Bereiche gemeinsam haben und eine intuitive Ansicht der Navigation verloren geht. Es ist unter anderem sehr schwierig, alle möglichen Statusänderungen zu verhindern, wenn der Benutzer hin und her navigiert.
Es kann gesagt werden, dass die Probleme der GUI-Programmierung ähnlich sind, bei der das Ereignismodell gut funktioniert, GUIs jedoch keine Navigation und keine Zurück-Schaltfläche haben. Das multipliziert die in Webanwendungen möglichen Zustandsübergänge. Das Ergebnis des Versuchs, dieses Problem zu lösen, sind schwere Frameworks mit komplizierten Konfigurationen, die viele allgegenwärtige magische Identifikatoren enthalten, ohne die Wurzel des Problems in Frage zu stellen: das Rückrufmodell und das inhärente Fehlen einer gemeinsamen Nutzung variabler Bereiche und keine Sequenzierung, daher muss die Sequenz durch Verknüpfen von Bezeichnern konstruiert werden.
Es gibt sequentiell basierte Frameworks wie ocsigen (ocaml) Seaside (Smalltalk) WASH (eingestellt, Haskell) und mflow (Haskell), die das Problem des Zustandsmanagements lösen und gleichzeitig die Navigierbarkeit und REST-Fülle beibehalten. Innerhalb dieser Frameworks kann der Programmierer die Navigation als eine zwingende Sequenz ausdrücken, in der das Programm Seiten sendet und auf Antworten in einem einzelnen Thread wartet, Variablen im Gültigkeitsbereich sind und die Zurück-Schaltfläche automatisch funktioniert. Dies erzeugt von Natur aus kürzeren, sichereren und besser lesbaren Code, bei dem die Navigation für den Programmierer klar sichtbar ist. (faire Warnung: Ich bin der Entwickler von mflow)
quelle
Die Frage ist ziemlich lächerlich, weil 1) Haskell dieses Problem bereits viel besser gelöst hat und 2) Erlang ungefähr auf die gleiche Weise wie Erlang. Hier ist der Benchmark gegen den Knoten: http://www.yesodweb.com/blog/2011/03/preliminary-warp-cross-language-benchmarks
Geben Sie Haskell 4 Kerne und es kann 100.000 (einfache) Anforderungen pro Sekunde in einer einzigen Anwendung ausführen. Der Knoten kann nicht so viele Anwendungen ausführen und eine einzelne Anwendung nicht über mehrere Kerne hinweg skalieren. Und Sie müssen nichts tun, um dies zu ernten, da die Haskell-Laufzeit nicht blockiert. Die einzige andere (relativ häufige) Sprache, in die nicht blockierende E / A in die Laufzeit integriert sind, ist Erlang.
quelle
pm2 -i max path/to/app.js
wird automatisch auf die optimale Anzahl von Instanzen skaliert, basierend auf den verfügbaren Kernen. Darüber hinaus blockiert Node standardmäßig nicht.Genau wie nodejs libev gelöscht hat, hat auch das Snap Haskell Web Framework libev gelöscht .
quelle