Anzeige
Anzeige
UX & Design
Artikel merken

Node.js: Das JavaScript-Framework im Überblick

JavaScript auf Serverseite. JavaScript auf Serverseite? Was absurd klingt, macht die Bibliothek Node.js möglich. In Verbindung mit HTML5-Technologien gelangt die Skriptsprache so zu gänzlich neuen Stärken, etwa in der asynchronen Programmierung. Eine Vorstellung.

9 Min. Lesezeit
Anzeige
Anzeige

Traditionelle Skriptsprachen wie beispielsweise PHP, die im Bereich der Web-Entwicklung zum Einsatz kommen, basieren auf Anfragen. Eine Anwendung wird also durch eine Anfrage des Clients gestartet. Diese Architektur erweist sich aber vor allem dann als unpraktisch, wenn es darum geht, einen Browser aktiv über Änderungen zu informieren. Eine Anforderung, die immer häufiger im Bereich von Realtime-Web-Anwendungen erforderlich ist.

Anzeige
Anzeige

Hier ergänzt serverseitiges JavaScript sinnvoll den Technologie-Stack einer Web-Anwendung, da eine JavaScript-Anwendung unabhängig von Anfragen der Clients auf dem Server läuft. Nach dem Start ist die Anwendung an einen bestimmten TCP-Port gebunden und wartet auf Anfragen der Clients. Sobald eine Verbindung aufgebaut ist, besteht die Möglichkeit, dass der Server aktiv mit dem Client kommuniziert.

Node.js

Eine konkrete Implementierung von JavaScript auf dem Server ist Node.js [1], das 2009 von Ryan Dahl ins Leben gerufen wurde und seit dieser Zeit stetig an Bekanntheit gewinnt. Den Kern bildet Googles V8-Engine, diese kommt auch als JavaScript-Engine in Googles Browser Chrome zum Einsatz.

Anzeige
Anzeige

Bislang kommt Node.js jedoch nur selten über die experimentelle Nutzung hinaus. Grund dafür ist zum einen das frühe Entwicklungsstadium, auf das auch die aktuelle Versionsnummer 0.8 hinweist. Es zeigt sich vor allem in häufigen Änderungen der APIs, was es Entwicklern schwer macht, immer die aktuelle Version zu verwenden, ohne die eigene Applikation grundlegend zu ändern. Doch die Entwicklungstendenz von Node.js geht hin zum Einsatz von Node.js in professionellen Web-Applikationen, vor allem im Bereich der bidirektionalen Kommunikation zwischen Client und Server.

Anzeige
Anzeige
Die serverseitige JavaScript-Implementierung Node.js gewinnt trotz frühem Entwicklungsstadium stetig Sympathien.
Die serverseitige JavaScript-Implementierung Node.js gewinnt trotz frühem Entwicklungsstadium stetig Sympathien.

Single- vs. Multi-Threading

Node.js setzt auf Single-Threading, es steht also lediglich ein Programm-Thread für die Anwendung zur Verfügung. In der Folge kann auch nur ein Kommando „gleichzeitig“ ausgeführt werden, was im ersten Schritt keine Parallelisierung erlaubt. Diese Architektur ist allerdings bewusst gewählt, da mit Multi-Threading auch die Anforderungen an den Entwickler steigen. So steht beim Multi-Threading nicht nur ein Thread für das Programm zur Verfügung, sondern mehrere, die sich im gleichen Programmkontext bewegen. Dabei muss etwa auf Thread-Safety, also die Steuerung des Zugriffs paralleler Threads auf eine Ressource, geachtet werden. Auch die Fehlersuche gestaltet sich bei dieser Art der Programmierung schwieriger, da bestimmte Arten von Fehlern nur bei einer bestimmten Konstellation und zeitlichen Abfolge von abhängigen Threads auftreten.

Um dennoch den Prozess nicht mit einer Anfrage für andere Anfragen zu blockieren, setzt Node.js auf ein ausgelagertes I/O-System. I/O steht für Input/Output, also Schreib- und Leseoperationen, sowohl auf der Festplatte als auch in der Datenbank. Während eine Anfrage bearbeitet wird, ist der Prozess für gewöhnlich blockiert. Aus diesem Grund wird I/O in Node.js ausgelagert und asynchron durchgeführt.

Anzeige
Anzeige

Den Kern der asynchronen Bearbeitung von Node.js bildet der Event Loop. Er nimmt Anfragen für I/O und Callbacks, also Funktionen, die ausgeführt werden, sobald I/O-Anfragen bearbeitet wurden, entgegen. Die Kontrolle erhält anschließend ein externer Prozess, Node.js kann sich um andere Operationen kümmern. Sobald der I/O-Prozess beendet ist, wird das Ergebnis an den Event Loop zurückgegeben, der den zuvor definierten Callback mit den Ergebnissen aufruft; die Bearbeitung läuft regulär weiter.

Die Kombination des ausgelagerten I/O-Systems mit Event Loop und dem Single-Threading macht Node.js zu einer äußerst performanten Lösung. Im Zusammenspiel mit verschiedenen anderen Technologien und Plattformen ist es deshalb ein idealer Bestandteil hochperformanter Web-Applikationen.

Websockets

In traditionellen Web-Applikationen antwortet der Server nur auf Anfrage des Clients. Es existiert kein Rückkanal, also keine Möglichkeit für den Server, den Client zu erreichen, falls sich Daten auf Serverseite verändert haben. Große Plattformen wie Facebook und Twitter leben jedoch von der Echtzeit-Interaktion der Nutzer. Dadurch entsteht die Notwendigkeit, neue Daten an verschiedene Clients auszuliefern, die die Darstellung der Daten im Idealfall partiell aktualisieren und so die Veränderungen widerspiegeln.

Anzeige
Anzeige

HTML5 standardisiert neben reinen HTML-Tags eine Reihe von Technologien, mit deren Hilfe die Anforderungen moderner Web-Applikationen besser erfüllt werden können. Im Fall der bidirektionalen Kommunikation zwischen Client und Server sind dies sogenannte Websockets [2], ein auf TCP aufbauendes Protokoll.

Bis alle gängigen Browser die für Node.js benötigten Websockets unterstützen, bietet sich die Socket.io-Bibliothek zur Überbrückung an.
Bis alle gängigen Browser die für Node.js benötigten Websockets unterstützen, bietet sich die Socket.io-Bibliothek zur Überbrückung an.

Das Protokoll operiert auf der gleichen Ebene wie HTTP. Der größte Unterschied zu HTTP liegt aber darin, dass es sich bei einem Websocket um eine permanente Verbindung handelt, über die sowohl der Client als auch der Server Daten schicken kann.

Problematisch ist dabei lediglich, dass diese Technologie nicht von allen Browsern unterstützt wird. Microsofts Internet Explorer etwa unterstützt das Websocket-Protokoll erst ab der nächsten Version (10). Bis es soweit ist, lässt sich das Protokoll mittels Bibliothek abstrahieren: Socket.io [3] ist komplett in JavaScript implementiert und steht sowohl auf Serverseite als auch auf Clientseite zur Verfügung. Unterstützt ein Browser keine Websockets, existiert ein Fallback auf Long Polling, Flashsockets und andere Technologien.

Anzeige
Anzeige

Installation von Socket.io

$ npm install socket.io

Listing 1

Um Socket.io in Verbindung mit Node.js verwenden zu können, muss das entsprechende Paket installiert sein. Node.js bietet hierfür den Node Package Manager (NPM) [4] an, mit dessen Hilfe sich einfach zusätzliche Module installieren lassen. Die wichtigsten Optionen für NPM sind entsprechend „install“ und „remove“, um Pakete zu installieren und wieder zu entfernen, sowie „list“, um die installierten Pakete samt Abhängigkeiten aufzulisten.

Node.js Socket Server

var io = require('socket.io').listen(1337);
io.sockets.on('connection', function (socket) {
    socket.on('message', function (data) {
        // do something
    });
});

Listing 2

Listing 2 zeigt, wie einfach ein vollständiger Socket-Server in Node.js aussieht. Prinzipiell wird der Server in drei Schritten erstellt. Zuerst wird das Socket.io-Modul mittels „require“ eingebunden und mit einem TCP-Port versehen, über den die Kommunikation erfolgt. Das Socket.io-Objekt, das dieser Aufruf zurückgibt, dient im weiteren Programmablauf dazu, das Verhalten des Servers festzulegen. Im nächsten Schritt definiert die Methode „on(‚connection‘, …)“ was passiert, sobald sich ein Client mit dem Server verbindet. Innerhalb der Callback-Funktion (zweiter Parameter) wird wiederum mittels „on“-Methode durch ein Callback festgelegt, was beim Eintreten bestimmter Ereignisse (im Beispiel beim Eintreffen einer Nachricht) auf dem Socket passieren soll.

Auf Serverseite besteht die Möglichkeit, über die „emit“-Methode Nachrichten über die Socket-Verbindung an den Client zu schicken. Dazu werden der Typ der Nachricht und weitere Daten angegeben, die die Nachricht spezifizieren.

Anzeige
Anzeige

Nachrichten über eine Socket-Verbindung senden

socket.emit('hello', { result: 'hello world' });

Listing 3

Wichtig dabei ist, dass „emit“ lediglich einen Client anspricht. Obwohl oft eine Point-to-Point-Kommunikation erwünscht ist, gibt es immer wieder Fälle, in denen an alle verbundenen Sockets oder eine bestimmte Gruppe von Sockets gesendet werden soll. Um alle Sockets zu erreichen, existiert die Methode „broadcast“ im Socket-Objekt, die wiederum die „emit“-Methode implementiert.

Eine Möglichkeit, um die größtmögliche Kontrolle über die verfügbaren Sockets zu erlangen, ist die Speicherung der Socket-Handles in einer Hashmap. Als Gruppierung der Sockets ermöglicht sie eine sehr feine Steuerung, auf welchen Socket zu welcher Gelegenheit welche Nachricht geschickt werden soll.

Wenn über die bestehende Socket-Verbindung persönliche Daten ausgetauscht werden, die für Dritte nicht einsehbar sein sollen, lässt sich von unverschlüsselten Websockets auf die sichere, verschlüsselte Variante zurückgreifen. Standardmäßig wird für Websockets das Protokollpräfix „ws://“ verwendet, bei der sicheren Variante „wss://“. Für eine explizit verschlüsselte Verbindung dient auf Clientseite einerseits das Präfix „https://“ in der „connect“-Methode, andererseits die Option „secure: true“.

Anzeige
Anzeige

Sichere Websocket-Verbindung

var socket = io.connect('https://example.com:1337', { secure: true });

Listing 4

Asynchrone Ausführung

Das zentrale Konzept von Node.js basiert also auf der asynchronen Abarbeitung in einem Single-threaded-Event-Loop, um aufwändige I/O-Operationen auszulagern. Bei eigenem Code ist man selbst dafür verantwortlich, Node.js nicht zu blockieren. Warum diese Überlegung durchaus relevant ist, zeigt ein einfaches Beispiel.

Synchrone Berechnung

require('http').createServer(function (req, res) {
    console.log('incoming request');
    var i = 0, x = 1;
    while(i++ < 1000000000) {
        x = 40 * 50 * i + 47;
    }
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello World');
    console.log('request answered');
}).listen(1337, '127.0.0.1');

Listing 5

Mit diesem Skript wird ein einfacher Webserver erstellt. Bei einer Anfrage gibt er auf der Konsole „incoming request“ aus. Die anschließenden Berechnungen sollen aufwändige Vorgänge innerhalb des Skripts simulieren, bevor der Server eine Rückgabe an den Client erstellt und verschickt, sowie auf der Konsole „request answered“ ausgibt. Der große Nachteil dieses Skripts besteht darin, dass während der Beantwortung eines Requests keine weiteren Anragen beantwortet werden können, da der Server durch die Berechnung blockiert ist. Die Ausgabe bei zwei annähernd gleichzeitig abgesendeten Requests ist entsprechend synchron (Listing 6).

Konsolenausgabe bei synchroner Berechnung

$ node server.js
incoming request
request answered
incoming request
request answered

Listing 6

Um die Berechnungen asynchron durchzuführen, bietet Node.js das „child_process“-Modul. Dieses Modul erlaubt im Zuge einer Applikation, Kind-Prozesse zu erstellen, in die verschiedene Aufgaben ausgelagert werden können. Eine bidirektionale Kommunikation zwischen Eltern- und Kind-Prozess ermöglicht darüber hinaus Events und Callbacks, die ein blockierendes Warten auf Antworten unnötig machen.

Anzeige
Anzeige

Listing 7 zeigt, wie eine einfache Lösung des Problems aussehen könnte. Es besteht im Prinzip aus den gleichen Komponenten wie das Beispiel der synchronen Berechnung. Der einzige Unterschied
besteht darin, dass der rechenintensive Teil in einer eigenen Datei liegt und als separater Kind-Prozess ausgeführt wird.
Durch das Kommando „fork“ wird aus dem Inhalt der angegebenen Datei ein
Kind-Prozess erzeugt. Mittels „send“ und „ok“ erfolgt die Kommunikation
zwischen den beiden Prozessen. Sobald der Eltern-Prozess eine
Rückmeldung erhält, wird die Antwort an den Client
gesendet.

Asynchrone Berechnung

// server.js
require('http').createServer(function (req, res) {
    console.log('incoming request');
    var task = require('child_process').fork('someTask.js');
    task.on('message', function (response) {
        res.writeHead(200, { 'Content-Type': 'text/plain' });
        res.end('Hello World');
        console.log('request answered');
    });
    task.send({ start: true });
}).listen(1337, '127.0.0.1');

// someTask.js
process.on('message', function(m) {
    var i = 0, x = 1;
    while(i++ < 1000000000) {
        x = 40 * 50 * i + 47;
    }
    process.send('finished');
});

Listing 7

Die Ausgabe dieses Skripts, ebenfalls mit zwei annähernd gleichzeitig abgesetzten Requests, ist in Listing 8 zu sehen. Es zeigt deutlich das Ergebnis einer asynchronen Bearbeitung. Dabei werden beide Requests sofort angenommen und bearbeitet. Sobald die
Berechnung abgeschlossen ist, erfolgt die Rückmeldung an den Client. Auf diese Weise wird das Skript nicht unnötig blockiert und
Node.js kann die Ressourcen besser ausnutzen.

Konsolenausgabe bei asynchroner Berechnung

$ node server.js
incoming request
incoming request
request answered
request answered

Listing 8

Ausblick

Die beiden Beispiele im Umgang mit Node.js zeigen, wie mächtig die Plattform ist. Verfolgt man das Konzept der Asynchronität konsequent, können mit einer Node.js-Instanz sehr viele Client-Anfragen bedient werden. Verschiedene Benchmarks sprechen in diesem Zusammenhang von mehreren zehntausend, manche gar von hunderttausenden parallelen Anfragen.

Aller Leistung zum Trotz entfaltet Node.js seine wahre Stärke jedoch erst im Zusammenspiel mit anderen Technologien. So empfiehlt es sich etwa, statische Inhalte von einem Nginx-Server ausliefern zu lassen und über einen Proxy-Server die Anfragen an Node.js weiterzuleiten.

Ein weiteres denkbares Szenario ist der Einsatz von Node.js in Verbindung mit Request-basierten Skriptsprachen wie beispielsweise PHP, um deren Schwächen bei bidirektionaler Echtzeit-Kommunikation auszugleichen. Durch die Kombination könnte so die Schwäche des einen (PHP) durch die Stärke des anderen (Node.js) ausgeglichen werden.

Die letzte stabile Version von Node.js verbesserte die Geschwindigkeit im Vergleich zur Vorgängerversion noch einmal deutlich. Das liegt unter anderem an einer aktualisierten Version der V8-Engine und einer Reihe von Erneuerungen verschiedener Kern-Bibliotheken.

An seine Grenzen stößt Node.js lediglich bei Überskalierung. Ist eine Instanz von Node.js mit den Anfragen überfordert, gibt es aber die Möglichkeit, über eine intelligente Proxy-Lösung die Anfragen von Clients auf verschiedene Node.js-Server zu verteilen. Der Applikationsstatus kann dann in einer Datenbank wie beispielsweise Redis zwischen den einzelnen Servern gespeichert werden.

Insgesamt zeigen all diese Denkanstöße, dass Node.js mit seinen Fähigkeiten noch lange nicht am Ende ist. Die Einsatzmöglichkeiten sind heute noch nicht überschaubar, einzig eines ist bislang klar: JavaScript auf Serverseite ist keineswegs absurd.

Mehr zu diesem Thema
Fast fertig!

Bitte klicke auf den Link in der Bestätigungsmail, um deine Anmeldung abzuschließen.

Du willst noch weitere Infos zum Newsletter? Jetzt mehr erfahren

Anzeige
Anzeige
6 Kommentare
Bitte beachte unsere Community-Richtlinien

Wir freuen uns über kontroverse Diskussionen, die gerne auch mal hitzig geführt werden dürfen. Beleidigende, grob anstößige, rassistische und strafrechtlich relevante Äußerungen und Beiträge tolerieren wir nicht. Bitte achte darauf, dass du keine Texte veröffentlichst, für die du keine ausdrückliche Erlaubnis des Urhebers hast. Ebenfalls nicht erlaubt ist der Missbrauch der Webangebote unter t3n.de als Werbeplattform. Die Nennung von Produktnamen, Herstellern, Dienstleistern und Websites ist nur dann zulässig, wenn damit nicht vorrangig der Zweck der Werbung verfolgt wird. Wir behalten uns vor, Beiträge, die diese Regeln verletzen, zu löschen und Accounts zeitweilig oder auf Dauer zu sperren.

Trotz all dieser notwendigen Regeln: Diskutiere kontrovers, sage anderen deine Meinung, trage mit weiterführenden Informationen zum Wissensaustausch bei, aber bleibe dabei fair und respektiere die Meinung anderer. Wir wünschen Dir viel Spaß mit den Webangeboten von t3n und freuen uns auf spannende Beiträge.

Dein t3n-Team

Seyreb

Frühes Entwicklungsstadium?
Was hat das mit dem Versionierungschema zu tun?
Node.js ommt auf jeder menge Seiten zum Einsatz, und das teilweie schon seit Jahren!
Habt ihr andere Artikel genauso schlecht recherchiert?

Antworten
Pascal192

Der Post ist doch an mehreren Stellen schon nicht mehr aktuell…

Antworten
Lars Budde

@Pascal Der Artikel stammt aus dem t3n-Magazin Nr. 29. Die Erstveröffentlichung war – wie du oben nachlesen kannst – am 28.08.2012. Möglicherweise erklärt das die fehlende Aktualität, die du bemängelst.

Antworten
nazeK

Node.js ist defintiv ausgereift. Die vielen Änderungen der Funktionen und Methoden (und damit auch der Dokumentation), sind seit Ende letzten Jahres m.M.n. nicht mehr das Problem. Natürlich muss, im Gegensatz zu PHP oder Ruby, der Code zur Zeit noch regelmäßig auf die Funktionalität geprüft werden (wenn man Node.js aktualisiert), da nicht unbedingt eine 100%ige Abwärtskompatibilität gegeben sein muss. Aber es ist nicht mehr so dramatisch wie vor einem Jahr.

Node.js ist eine sehr attraktive Alternative (ja, Alternative) zu Ruby, PHP und co. Applikationen wie Calipso zeigen eindrucksvoll, was wirklich mit Node.js machbar ist. Ich bin gegenwärtig am überlegen, warum man noch bei PHP bleiben sollte. Besonders bei Anwendungen die höherer Last ausgesetzt sind und wo man auch nur eine Anwendung auf einem Host betreibt, gebe ich Node.js den Vorzug. Für multi-node.js-Umgebungen (also Server auf denen mehrere Instanzen bzw. Anwendungen laufen), habe ich bisher noch nicht das Tool gefunden, welches das Organisieren ebendieser Anwendungen bequem ermöglicht (es gibt einige Tools, bisher aber noch nicht so bequem, dass Lieschen Müller damit umgehen könnte). Da bleiben Ruby und PHP *noch* erste Wahl. Ich kann mir aber vorstellen, dass sich dies bald ändern könnte.

Was natürlich auch beachtet werden muss, ist die Schwerfälligkeit der Using-Community. Also jener Webmaster, Hoster und IT-Anbieter, die später Node.js einsetzten müssten. Das Beispiel Nginx zeigt ja, dass bessere Software nicht unbedingt ein Argument für einen Umstieg ist. Anders kann man die herrschende Dominanz von Apachewebservern nicht erklären. Ähnlich kann/wird es wohl auch Node.js gehen. Ein Tool für Nerds, Geeks & Enterprise-Entwickler.

Antworten
asmuelle

Ich habe zur Zeit 5 Webapplikationen im Einsatz, die skalieren deutlich besser als ihre Tomcat Vorgänger und sind sehr stabil.

Antworten
areanet

Wer mehr über die technische Umsetzung von node.js und Co. erfahren will, für den haben wir ein fünfteiliges Tutorial zum Thema „Entwicklung einer Echzeit-Multiscreen-App“ in unserem Blog zusammengestellt: http://www.app-agentur-bw.de/blog/multiscreen-tutorial-chat-mit-websockets-teil-1-einfuehrung

Antworten

Melde dich mit deinem t3n Account an oder fülle die unteren Felder aus.

Bitte schalte deinen Adblocker für t3n.de aus!
Hallo und herzlich willkommen bei t3n!

Bitte schalte deinen Adblocker für t3n.de aus, um diesen Artikel zu lesen.

Wir sind ein unabhängiger Publisher mit einem Team von mehr als 75 fantastischen Menschen, aber ohne riesigen Konzern im Rücken. Banner und ähnliche Werbemittel sind für unsere Finanzierung sehr wichtig.

Schon jetzt und im Namen der gesamten t3n-Crew: vielen Dank für deine Unterstützung! 🙌

Deine t3n-Crew

Anleitung zur Deaktivierung
Artikel merken

Bitte melde dich an, um diesen Artikel in deiner persönlichen Merkliste auf t3n zu speichern.

Jetzt registrieren und merken

Du hast schon einen t3n-Account? Hier anmelden

oder
Auf Mastodon teilen

Gib die URL deiner Mastodon-Instanz ein, um den Artikel zu teilen.

Anzeige
Anzeige