Node.js: Ein kommender Cloud Standard?

Über keine der aktuellen Programmiersprachen ist in der letzten Zeit soviel gesprochen worden, wie über Node.js. Aber warum ist dies der Fall? Dieser Artikel versucht eine Einordnung, einen Überblick und einen Ausblick zu geben.

Die Grundlagen 

Prinzipiell ist es für Programmierer einfach eine neue Programmiersprache zu erlernen, wenn man bereits eine Sprache zur Gänze durchdrungen hat. Professoren nutzen gerne auch eine Aussage, wie “Gute Programmierer können in jeder Sprache programmieren, denn wenn man einmal weiß, wie man programmieren muss, dann ist der Rest nur eine Frage der Syntax”. Bei Node.js ist die Sachlage sogar noch ein wenig komfortabler, da viele Web Programmierer bereits mit Javascript vertraut sind und in den letzen Jahren kaum andere Programmiersprachen nutzen mussten, da die Frameworks und Erweiterungen der Sprache dies nicht notwendig machten. Dies ist ein Grund, warum gerade der Sprung für viele Programmierer von Javascript zu Node.js relativ leicht ist. Die Hürde sich mit einer neuen Syntax beschäftigen zu müssen entfällt nahezu. 

Wir halten fest: +1 für Node.js

Ein Blick auf Node.js 

In letzer Zeit wurden so einige Blogeinträge über Node.js verfasst. Darum soll dieser Abschnitt nur einen nitty-gritty-Überblick geben. Tiefergehendere Artikel und Blogs finden Sie in den Quellenangaben zu diesem Artikel. Node.js basiert auf der Googles V8 Javascript Engine und ermöglicht das Schreiben von server-seitigen, event-basierte und asynchrone Anwendungen mit Hilfe von Javascript. Die V8 Engine von Google wurde in C++, Javascript und Assembler verfasst, welches die Performance der Engine Rechnung trägt. Der in Javascript verfasste Code wird vor der Ausführung in nativen Maschinencode übersetzt. Weitere Feinheiten, wie Inline Caching erhöhen noch die Performance. Man kann also sagen, dass die Geschwindigkeit des Grundgerüsts von Node.js Vorfreude bereitet. 

Eines der erklärten Ziele von Node.js ist es eine Sprache zu komponieren, die das einfache erstellen von skalierbaren netzwerkfähigen Programmen ermöglicht. Dies sind gleich mehrere harte Herausforderungen für eine Programmiersprache. Ein netzwerkfähiges Programm zu erstellen ist per se nicht schwierig und auch in der ein oder anderen Programmiersprache recht einfach und effizient. Und eine einfach zu erlernende Sprache zum Lösen von netzwerkorientierten Problemen auf Serverseite ist sicherlich für jeden Web Programmierer ein Zugewinn. Diesen Anspruch kann und wird Node.js sicherlich erfüllen. 

Doch wie sieht es mit der so wichtigen Skalierung aus? 

Kampf der Nebenläufigkeit

Betrachten wir zunächst ein einfaches Beispiel aus dem Alltag. Stellen wir uns einmal vor, wir möchten einen kleinen Imbiss in einem Fastfood Restaurant zu uns nehmen. Dazu begeben wir uns zunächst i das präferierte Restaurant und stellen und in die Warteschlange. Wie lang diese ist, ist sicherlich je nach Tageszeit und bedingt durch andere Einflüsse, wie etwa Urlaubszeiten unterschiedlich. 

Die Thread-basierte Methode das Restaurant zu betreiben würde bedeuten, dass ein Kunde aus der Warteschlange (FIFO) bedient wird, sobald er an die Theke vorgerückt ist, also die notwendigen System-Ressourcen einmal zugewiesen bekommen hat. Je nach Implementierung des Kunden ( :-) ) kann dieser Vorgang nun wenige Sekunden dauern, oder auch Minuten. Solange jedoch der Kunde die Ressourcen besitzt, gibt er diese auch nicht mehr ab. Das würde zu einem starken Anwachsen der Warteschlange führen, wenn man nicht entsprechende Maßnahmen ergreift. Dies würde bedeuten, dass ich einen weiteren Verkäufer (Thread) hinzufügen müsste, um meine Blockierung etwas zu entschlacken und die Warteschlange möglichst klein zu halten. Dies bedeutet jedoch in unserem Beispiel eine starke finanzielle Aufwendung, die auch nicht an die Dynamik der Anfragen gekoppelt ist und uns somit eine prinzipielles Problem aufwirft. Dies ist besonders deutlich, wenn der Ansturm an Kunden besonders gering ist. Es gibt sicherlich viele Möglichkeiten dieses Problem mit Thread-basierten Lösungsansätzen in den Griff zu bekommen und eine möglichst effiziente, Ressourcen schonende und skalierbare Implementierung zu Implementieren.

In der asynchronen und ereignisbasierten Welt würde der Kunde in das Restaurant gehen, sich in die Warteschlange einreihen. Beim Zuteilen der Ressourcen bekäme der Kunde jedoch eine Liste mit den zu erledigenden Dinge, wie z.B. eine Speisekarte und optionalen Features; wie Verzehr vor Ort oder Auswärts, Ketchup, Mayonnaise, Extra Käse - die üblichen zeitfressenden Fragen bei einer Bestellung. Der Kunde würde daraufhin die Warteschlange verlassen und sich erst wieder einreihen, wenn er eine Bestellung ohne Rückfragen des Kellners abgeben kann. Persönliche Bemerkung an dieser Stelle: Ich versuche dies seit Jahren in die Realität umzusetzen. Es funktioniert jedoch nur, wenn die Prozesse des Unternehmens, bei dem ich eine Bestellung aufgebe wohl definiert sind, die Mitarbeiter bestens geschult sind und ich die Speisekarte und die Features selbst bestens kenne. Denn dann ist es in der Tat möglich den Bestellvorgang auf eine Guten Tag-Ihre Bestellung bitte-Das macht dann-Auf Wiedersehen-Zyklus zu begrenzen, der wenige Sekunden dauert. Dies ist jedoch nicht immer möglich. Doch zurück zu unserer asynchronen Welt. Der Kunde (Prozess) würde also wieder zurück an die Theke kommen, wenn er meint alle Anfragen an die Bedienung korrekt stellen zu können. Wir gehen in diesem vereinfachten Modell von einem sauber implementieren System auf Seiten des Restaurants aus. Wenn der Kunde die Anfrage nicht korrekt formuliert, dann muss er sich wieder zurückziehen und es später erneut versuchen. Dies wiederholt sich, bis der Bestellvorgang abgeschlossen ist. 

Doch wie sieht es in diesem Fall mit der Skalierbarkeit aus? Sicherlich kann man auch in diesem Fall eine weitere Bedingung im Falle eines zu starken Anwachsens der Warteschlange hinzufügen. Jedoch ist es nicht so komfortable, wie die Thread-basierte Lösung. Vorteile dieser asynchronen und nicht blockierenden Methode gibt es viele, einer ist zum Beispiel, dass die Bedienungen nicht durch einen Kunden blockiert werden. 

Welcher der beiden Ansätze ist nun jedoch der bessere? Nun darüber streiten sich die Fachlektüren rauf und runter und dies nicht erst seit gestern. Aus der Prozesstheorie für Arbeitsabläufe in Firmen könnte man zum Beispiel anführen, dass es wesentlich effizienter ist, wenn sich ein Mitarbeiter zu 100% auf eine Tätigkeit konzentriert und diese ohne Störung und Unterbrechung abarbeitet. In der Realität, könnte man aber gegenargumentieren, kommt es aber zu Unterbrechungen, wie ein Telefonanruf. Schön wäre es doch alle Vorteile beider Welten zu kombinieren und einzusetzen. Doch genau da sind wir an der Schwachstelle von Node.js angekommen. Momentan ist das Ziel nur die asynchrone und nichtblockierende Implementierung voranzutreiben. Damit hat man keinerlei Möglichkeit mehr auf Änderungen in dem Workload, der Anwendung oder der Performance durch einen Wechsel oder eine Vermischung von beiden Modellen, wie es in anderen Sprachen wie C++ oder Java möglich ist. 

Neue Software für eine neue Welt?

Die Frage, welche man sich heute stellen muss, ist jedoch, ob es notwendig ist diese Modelle zu mischen. Ist es im Zeiten des Cloud Computing nicht vielmehr notwendig über grundlegende Prinzipien nachzudenken? Was ist Skalierung? Was bedeutet Auto-Skalierung? Wie wird es heutzutage betrieben? Sind die aktuellen Anwendungen überhaupt Cloud-fähig, oder müssen nicht vielmehr viele Anwendungen überarbeitet oder gar neu implementiert werden? Cloud Computing in seiner jetzigen Form unterstützt beide Welten und auf dem Weg zu neuen skalierbaren Applikationen ist es notwendig über die grundlegenden Modell einmal in Ruhe nachzudenken. Betrachten wir doch einmal die aktuellen Infrastruktur als ein Dienst Anbieter. In der Regel bieten sie beginnend von einer kleinen Maschine, welche in Ausstattung und Performanz gerade einmal einem Netbook entspricht. Um nun die vollen Nutzen aus einer solch kleinen Maschine zu ziehen, um Skalierung und Kosten im Griff zu haben, müssen wir uns fragen, wie die Skalierung unseres Systems aussehen könnte und wie wir die beste Performanz erreichen? Im Vergleich dazu haben wir auf der Enterprise IT Seite hochperformante Systeme mit enormen Speicher und Mehrkernsystemen. Auf den ersten Blick dürfte jedem auffallen, das es zwischen den beiden Welten einen Unterschied gibt, der sich sehr wahrscheinlich auch auf das Programmiermodell niederschlagen dürfte. Mit dynamischen Ansätzen kommen wir auf den Enterprise IT Maschinen sehr gut zurecht. Mal benötigt man mehr Thread-basierte Lösungen und mal kippt das Programm mehr zu einer asynchronen und nicht-blockierenden Lösung. Bei unseren kleinen Cloud Computing Maschinen ist dies anders. Wie haben es hier mit einer kleinen Single Core CPU zu tun. Und diese sollte möglichst gut ausgelastet werden. Wenn wir die CPU jedoch auslasten möchten und diese für unsere Paar Cent ideal nutzen wollen, sollten wir dann nicht versuchen die Last so hoch wie möglich zu bekommen? Und ist dies nicht der Fall, wenn die I/O Operationen nicht blockierend sind? Denn nur wenn möglichst viele Prozesse die Chance haben die Ressource zugeteilt zu bekommen, dann kann die CPU auch beschäftigt gehalten werden. Wenn wir noch tiefer hineinschauen, dann stellen wir fest, dass die Architektur von Infrastruktur als ein Dienst Datenzentren so aufgebaut ist, dass viel I/O Kommunikation stattfinden muss, um beispielsweise Prozesse zu synchronisieren. 

Fazit 

Node.js ist ein vielversprechender Kandidat für eine Standardsprache im Cloud Computing. Jedoch ist abzuwarten, wie die Skalierung der kommenden Systeme funktionieren wird. Mit C++ als Grundgerüst ist auf jeden Fall Performanz und Skalierbarkeit gegeben, man denke nur an OpenMP. Mit mehr als 2500 Repositorien, welche bereits von der Gemeinde implementiert worden sind ist auf jeden Fall ein weiterer Grundstein gelegt. Ferner ist die Sprachsynchronität auf der Seite von Client und Server ein nicht zu unterschätzender Vorteil, auch wenn Web Dienste generische Schnittstellen bereitstellen sollten und damit die Sprachabhängigkeiten verschwinden lassen sollten. Jedoch ist die Implementierung der Applikationen in der Realität nicht weit genug darauf abgestimmt und auch Standards wie WSDL 2.0 haben sich noch nicht in den Anwendungen durchgesetzt. Wie immer ist technologisch vieles schöner vorhanden, was sich jedoch durchsetzen wird ist wie immer unklar. Ich denke das die Möglichkeiten, welche Node.js mit sich führt überwiegen dürften. Auch werden in Zukunft die Programme sauberer entwickelt werden, da der Client- bzw. Web Client-Entwickler nun auch die Serverseite implementieren kann. Dies ist insofern entscheidend, als dass sich die Entwickler besser austauschen und verständigen können, der Code in einer anderen Programmiersprache immer nicht so gut ist, wie in der Haussprache eines Entwicklers. Hinzu kommt, dass sich die Art, wie Programme auf Cloud Computing Infrastrukturen entwickelt werden deutlich in Richtung von Technologien wie Node.js verlagern dürfte. Auch ist die momentan betrieben automatische Skalierbarkeit nicht wirklich schön gelöst. Vielleicht hat Node.js auch dafür eine Lösung in Zukunft bereit. Wir dürfen gespannt sein.

 

Quellen:

[1] Node.js (nodejs.org)

[2] Google‘s V8 JavaScript Engine (en.wikipedia.org)

[3] 2500+ repositories for Node.js related code (github.com)     

Technology Evangelist Developer
Research Engineer Strategist
Trainer
Platform Architect
{Mobile, Cloud & Grid}

view archive