Zeitgemäße Webanwendungen in JavaScript entwickeln

Werkzeuge  –  8 Kommentare

Node.js ist ein Framework zur Entwicklung serverseitiger Webanwendungen in JavaScript, das sich besonders gut für skalierbare, hochperformante und echtzeitfähige Webanwendungen eignet. Zudem ermöglicht es Webentwicklern die einheitliche Verwendung von JavaScript für Client und Server. Dieser Artikel erklärt, was Node.js auszeichnet, und erläutert die grundlegenden Konzepte.

Der Begriff Node.js geistert seit 2009 durch die IT-Fachpresse und wird mit einer gewissen Regelmäßigkeit als "das nächste große Ding" bezeichnet. Erfinder von Node.js ist Ryan Dahl, der das Framework 2009 erstmals veröffentlicht hat. Einen besonderen Schwerpunkt legt Node.js auf die einfache und zugleich hohe Skalierbarkeit von Webanwendungen, um dem ständig wachsenden Bedarf an zahlreichen gleichzeitigen und dauerhaften Verbindungen gerecht zu werden.

Da Node.js Googles schnellen JavaScript-Compiler V8 enthält, verfügen die entwickelten Webanwendungen über eine hohe Performance. Der funktionale Ansatz von JavaScript ermöglicht darüber hinaus eine gute Parallelisierbarkeit der Ausführung. Zudem können auch echtzeitfähige Webanwendungen entwickelt werden, da WebSockets und Streaming fundamentale Konzepte in Node.js sind. Mit wenigen Zeilen JavaScript-Code lassen sich deshalb leistungsfähige und zeitgemäße Webanwendungen erstellen, die Desktop-Programmen nicht nachstehen.

Entwicklung und Ausführung erfolgen dabei plattformunabhängig: Node.js steht ohne zeitaufwendige Installation für Linux, Mac OS X und Windows zur Verfügung und lässt sich mit den gängigen Texteditoren und zahlreichen integrierten Entwicklungsumgebungen verwenden.

Unterstützt wird Node.js von der Firma Joyent und einer engagierten Community. Diese hat innerhalb von zweieinhalb Jahren ein umfassendes Ökosystem mit etwa 15.000 Komponenten für nahezu jeden Anwendungsfall geschaffen und arbeitet stetig an weiteren Erweiterungen und Ergänzungen.

Mit der zunehmenden Verbreitung von Breitbandtechniken, mobilen Endgeräten und der zeitnahen Bereitstellung von Daten steigen die Ansprüche, die Anwender an Webanwendungen stellen. Fasst man diese in Kategorien zusammen, ergeben sich drei grundlegende Forderungen, die zeitgemäße Implementierungen von den Vertretern aus den Zeiten des Web 1.0 und 2.0 unterscheiden:

  • Moderne Webanwendungen müssen weitaus besser skalieren, um das C10K-Problem lösen und 10.000 Clients gleichzeitig bedienen zu können.
  • Sie müssen eine hohe Performance aufweisen, sodass sie einer nativen Anwendung weder in Komfort noch in Reaktivität nachstehen.
  • Zeitgemäße Webanwendungen müssen im Idealfall echtzeitfähig sein, um den Benutzer so schnell wie möglich mit neuen Informationen zu versorgen.

An diesen Punkten setzt Node.js an.

Node.js löst das C10K-Problem, indem es einen anderen Ansatz verfolgt als klassische Webserver: Statt für jede eingehende Anfrage einen eigenen Thread zu verwenden, verarbeitet das Framework sämtliche Anfragen eines Threads nacheinander. Damit der nicht blockiert, muss er seine Arbeit nach Möglichkeit delegieren.

Hierbei kommt Node.js zugute, dass die meisten Webanwendungen Abhängigkeiten auf externe Ressourcen wie das Dateisystem oder eine Datenbank aufweisen und die Anfragen an diese Ressourcen zeitaufwendig sind. Im Gegensatz zu einem klassischen Webserver, dessen Threads die meiste Zeit mit Warten auf die Beendigung einer I/O-Operation beschäftigt sind, verwendet Node.js sogenanntes asynchrones oder nichtblockierendes I/O.

Asynchrones I/O bedeutet, dass der Node.js-Thread eine eingehende Anfrage bis zur ersten Interaktion mit einer externen Ressource verarbeitet, dann diese Interaktion startet und die Anfrage so lange beiseite legt, bis eine Antwort von der Ressource vorliegt.

In der Zwischenzeit kümmert sich Node.js um die nächste Anfrage und verarbeitet diese bis zur Interaktion mit einer externen Ressource und so weiter. Sobald die Antwort vorliegt, wird ein Callback ausgeführt und die Anfrage fortgesetzt: entweder bis zur nächsten Interaktion mit einer externen Ressource oder bis zu deren Abschluss. Auf diese Art kann Node.js mit einer weitaus höheren Anzahl von Anfragen umgehen, als das für einen klassischen Webserver je möglich wäre.

Da Node.js sämtlichen Code per se asynchron ausführt, ist bedeutend weniger Aufwand in die Nebenläufigkeit einer Anwendung zu investieren, was die Entwicklung in Node.js auch für Programmierer mit wenig Erfahrung in der Parallelisierung verhältnismäßig vereinfacht. Node.js selbst stellt jedoch keine vollständige Laufzeitumgebung dar, sondern lediglich ein Framework für asynchrones I/O, das eine Klassenbibliothek zur Entwicklung serverseitiger Webanwendungen ergänzt.

Als Laufzeitumgebung dient V8 , die somit für den Entwickler de facto transparent ist. Deren Verwendung trägt einen großen Anteil zur Ausführungsgeschwindigkeit von Node.js bei, was in Verbindung mit dem zuvor genannten Modell zur Skalierbarkeit eine performante Umgebung ergibt.

Node.js schätzt zudem das HTTP-Protokoll, weshalb es dieses nicht durch unnötige Abstraktionsschichten vor dem Entwickler zu verbergen versucht. Dadurch wird zum einen eine direkte Art der Entwicklung möglich – eine Herangehensweise, die Webentwickler durchaus schätzen. Schließlich achtet das Web selbst die gleiche Kultur: Einfache, voneinander unabhängige Bausteine genügen, wobei die eigentliche Macht aus der flexiblen Kombination der Einzelteile entsteht. Zum anderen ermöglicht Node.js damit auch den direkten Zugriff auf den ein- und ausgehenden Datenstrom, sogar während der Datenübertragung, was es beispielsweise für Streaming prädestiniert.

Auch andere Echtzeitszenarien lassen sich in Node.js umsetzen: Die Unterstützung von WebSockets ermöglicht beispielsweise Anwendungen, bei denen ein Webserver nach der abgeschlossenen Übertragung einer Webseite an den Client noch Informationen im Push-Verfahren senden kann. Für all das wird eine Programmiersprache genutzt, die vielen Webentwickler vertraut ist: JavaScript. Für Node.js entwickelte Webanwendungen sind in JavaScript geschrieben. Im Gegensatz zu klassischem JavaScript findet die Ausführung nun allerdings auf dem Server und nicht auf dem Client statt.

JavaScript-Code lässt sich mit Node.js außer auf dem Client nun auch auf dem Server verwenden. Insbesondere Code, den ohnehin beide Instanzen ausführen, drängt sich dafür auf. Das Paradebeispiel hierfür stellt Validierungslogik dar, die aus Komfortgründen zunächst auf dem Client, aus Sicherheitsgründen aber auch auf dem Server auszuführen ist. Diese ist bei Verwendung von Node.js nur einmal zu implementieren und lässt sich dann auf beiden Seiten nutzen.

Nicht nur das HTTP-Protokoll ist in Node.js ein Bürger erster Klasse, auch das Datenformat JSON (JavaScript Object Notation) zählt als solcher. Das bedeutet, dass sich Node.js besonders zur Implementierung von mit JSON arbeitenden APIs oder Webdiensten eignet, die dem REST-Paradigma (Representational State Transfer) folgen. Selbiges gilt für Webanwendungen, die lediglich aus einer einzigen HTML-Seite bestehen und ihre weitere Logik in JavaScript abbilden.

Als Faustregel zur Benutzung von Node.js können die genannten Kriterien Skalierbarkeit, Performance und Echtzeitfähigkeit gelten: Sobald eine dieser Eigenschaften unverzichtbar für den Erfolg einer Webanwendung ist, stellt es höchstwahrscheinlich eine geeignete Plattform für deren Implementierung dar.

In einigen Fällen ist der Einsatz von Node.js allerdings ungeeignet oder zumindest wenig nützlich: Findet kaum Interaktion mit externen Ressourcen statt, wird der Thread unnötig blockiert, und die eingehenden Anfragen müssen eventuell lange warten. Das Verhalten gestaltet sich je schlimmer, desto rechenintensiver eine Webanwendung ist. Typische CRUD-Anwendungen (Create, Read, Update, Delete), die Daten ohne größere Verarbeitung lediglich zwischen Webbrowser und Datenbank übertragen, sind ebenfalls ungeeignet.