© DrHitch/Shutterstock.com
JavaScript für .NET-Entwickler

3 Webserverprogrammierung mit Node.js


Mit Node.js findet JavaScript auch auf dem Webserver eine immer größere Verbreitung, denn Webentwickler können damit auf Client und Server die gleiche Programmiersprache verwenden. Zudem zeichnet sich Node.js gerade durch den Verzicht auf Abstraktion und eine hohe Skalierbarkeit aus. Dieses Kapitel stellt die Grundlagen von Node.js dar, gibt einen Überblick für die wichtigsten Node.js-Erweiterungspakete und behandelt auch die Integration von Node.js mit .NET über Edge.js.

Die ersten Versionen von Node.js [1] stammen vom Webentwickler Ryan Dahl aus dem Jahr 2009. Er machte sich damals auf die Suche nach einem effizienten Webserverframework, das sehr viele Anfragen parallel bearbeiten kann, zum Beispiel vor dem Hintergrund einer Vielzahl von Clients, die vom Server einen Status via Polling erfragen müssen. Nachdem er bei einigen Versuchen auf der Basis der Programmiersprachen C, Lua und Haskell gescheitert war, kam er zu JavaScript, das auf dem Prinzip der asynchronen, nicht blockierenden Operationen basiert. Da JavaScript aber kein API für externe IO-Operationen enthält, entwickelte er zu diesem Zweck einen einfachen Abstraction-Layer und eine Core-Bibliothek, die selbst in JavaScript geschrieben ist. Dies gab ihm die Möglichkeit, konsequent auf nicht blockierende und ereignisgetriebene IO-Operationen zu setzen.

Node.js verwendet nur einen einzigen Prozess und nur einen einzigen Kern-Thread für die Ausführung von JavaScript-Code, was also heißt, dass Node.js eigentlich immer nur genau eine Anfrage zu einem Zeitpunkt bearbeiten kann. Alle anderen Anfragen müssten warten. Dennoch kann Node.js viele Anfragen scheinbar gleichzeitig bearbeiten, denn Node.js delegiert viele Aufgaben direkt an das Betriebssystem oder die Datenbank, d. h. Node.js schickt z. B. das Kommando zum Schreiben der Datei an das Betriebssystem oder das Aktualisieren eines Datensatzes an die Datenbank und registriert einen „Callback“, mit dem das Betriebssystem bzw. die Datenbank Node.js darüber informiert, dass die Aktion durchgeführt worden ist und Node.js nun die nächsten Schritte starten kann. Währenddessen kann Node.js eine andere Anfrage aus der Node.js Event Queue (Abb. 3.1) bearbeiten. Dies kann entweder ein Callback eines anderen Threads sein (siehe rote Anfrage in Abb. 3.1) oder eine ganz neue Anfrage (siehe gelbe Anfrage in Abb. 3.1). Alle Aufgaben für den Node.js-Kern-Thread laufen über die Event Queue. So muss sich auch der grüne Callback des Datenbank-Threads in die Event Queue stellen, wo er dann aber ohne Wartezeit entnommen und vom Node.js-Kern-Thread ausgeführt wird, da dieser gerade nichts zu tun hat.

schwichtenberg_nodejs_1.png

Abbildung 3.1: Architekturschaubild der asynchronen Architektur von Node.js

Dieses Node.js-Prinzip bedeutet auch, dass Node. js immer dann sehr gut geeignet ist, wenn eine Webentwicklung viele IO-Operationen nutzt. Node.js ist hingegen schlecht für Anwendungen, bei denen Berechnungen direkt im Webserver stattfinden sollen. Solche Berechnungen würden alle anderen Anfragen aufhalten. Node. js sollte solche Tätigkeiten immer an einen anderen Thread oder Prozess delegieren. Die in Node.js eingesetzte JavaScript-Engine ist Googles V8.

Node.js ist ein Open-Source-Projekt, dessen Entwicklung von der Firma Joyent [2] finanziell und durch eigene Entwickler im Core-Team unterstützt wird. Insgesamt haben schon mehr als 500 Entwickler Beiträge zum Kern von Node.js geliefert [3]. Als so genannter „Gatekeeper“ fungiert mittlerweile Isaac Schlueter, ein angestellter Entwickler bei Joyent. Joyent betreibt zusammen mit Nodejitsu auch die Node.js-Erweiterungsmodulbibliothek „Node Package Manager“ (npm) [4], in der Anfang Dezember 2013 schon mehr als 53 000 Erweiterungspakete registriert waren [5]. Die große Zahl der Erweiterungspakete ist aber unter drei Blickwinkeln zu bewerten: Erstens sind die Kernfunktionen von Node.js sehr überschaubar. Zweitens gibt es viele Erweiterungen, die nur eine Hand voll Funktionen anbieten. Drittens gibt es viele Überschneidungen in den Erweiterungen.

Die betrachtete Version von Node.js trägt die Versionsnummer 0.10.24. Dass die Version mit einer 0 beginnt, basiert auf der Tatsache, dass sich die Entwickler der Node.js-Kernbibliothek immer noch Änderungen an den APIs vorbehalten. Erst wenn diese Änderungen abgeschlossen sind, soll es eine Version 1.0 geben.

Aufgrund seines einfachen Entwicklungskonzepts, der Möglichkeit, auf die große Menge an hochwertigen und leistungsfähigen Bibliotheken zugreifen zu können, und nicht zuletzt der Verwendung von JavaScript auf dem Client (Browser) und dem Server, ist der Einsatz von Node. js mittlerweile bei Webanwendungen eine beliebte Wahl. Die Entwicklung von Webanwendungen ist dabei aber nicht die einzige Domäne, in der Node. js zum Einsatz kommt, dies zeigt die Entwicklung eines SMTP-Servers [6] von craigslist [7], der mehr als 50 000 000 Benutzer bedienen kann. Auch Firmen wie PayPal haben den einfachen Entwicklungsstil von Node. js zu schätzen gelernt und steigen von der bisherigen Java-Entwicklung um [8].

Es gibt inzwischen zahlreiche Hosting-Angebote für Node.js, die die Website [9] auflistet. Darunter ist auch Microsofts Cloud-Angebot Windows Azure. Das „Node. js Developer Center“ [10] zeigt, wie man Node. js in Windows Azure nutzen kann. Zur Unterstützung bietet Microsoft ein Erweiterungspaket mit Namen „Azure“ [11] an, das den Zugriff auf Azure-Dienste wie Table Storage, Blog Storage, Queues, Service Bus, DH Insight, SQL Azure, Notification Hubs und Service Runtime unterstützt.

Um mit der Entwicklung beginnen zu können, ist zuerst die aktuelle Version von Node.js von [1] zu laden. Die unterstützten Plattformen von Node.js umfassen alle heute gängigen Betriebssysteme (Linux, Mac OS X, Windows, Solaris). Bei der Windows-Installation sollte man die Frage, ob der Pfad zu node.exe und dem Paketmanager zu den Umgebungsvariablen hinzugeführt werden sollen, mit „Ja“ beantworten, um später Schwierigkeiten bei der Verwendung zu vermeiden. Eine Konfiguration für den Node.js-Prozess erfolgt über Windows-Umgebungsvariablen. Es haben sich zur Konfiguration des Ports und der Umgebung die beiden Variablen PORT und NODE_ENV etabliert. Diese können dann aus dem Node.js-Programm mit den Prozessvariablen process.env.PORT und process.env.NODE_ENV abgerufen werden.

Hello World in Node.js

Zur Erstellung des ersten Node.js-Programms reicht ein einfacher Texteditor. Listing 3.1 implementiert einen Webserver, der bei allen Anfragen „Hallo Welt“ zum Browser liefert. In Listing 3.1 wird in der ersten Zeile das HTTP-Modul aus der Standardbibliothek von Node.js eingebunden. Mit diesem Modul wird die Erstellung von HTTP-Servern ermöglicht, das Empfangen und das Versenden von Daten sowie der Umgang mit den HTTP-Objekten vereinfacht. In den Zeilen 3 bis 6 wird eine JavaScript-Funktion anfrageBearbeiter definiert, mit der die Anfragen bearbeitet werden. In dieser wird ein HTTP-Header erzeugt und danach der Text Hallo Welt als Inhalt gesendet.

var http = require('http');

// aus der Anfrage die Antwort erzeugen
var anfrageBearbeiter = function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('Hallo Welt\n');
res.end(); }

// Server erzeugen
var server = http.createServer(anfrageBearbeiter);
// auf einem Port lauschen
server.listen(3000, function() {
console.log("Server lauscht auf http://localhost: 3000");
});

Listing 3.1: Minimaler HTTP-Server mit Node.js

Das eigentliche Hauptprogramm beginnt in Zeile 8 mit der Erstellung einer HTTP-Serverinstanz, die die zuvor erstellte Funktion anfrageBearbeiter als Parameter erhält. Mit Zeile 10 wird der Server gestartet, um auf dem Port 3000 zu lauschen und Anfragen entgegenzunehmen, zusätzlich wird eine Callback-Funktion übergeben, die dann, wenn das Starten des Servers erfolgreich war, eine Ausgabe auf der Konsole erzeugt. Um diesen HTTP-Server zu starten, ruft man ihn nun einfach mit der Anwendung node.exe an der Kommandozeile auf: C:\Program Files\nodejs\node.exe H:\TFS\Demo\JS\nodeJS\NodejsWeb\NodejsWeb\1b HelloWorldServer.js.

Listing 3.2 zeigt schon einen etwas realitätsnäheren HTTP-Server, der einen URL-Parameter mithilfe der Kernbibliothek url ausliest und diesen Wert in die Ausgabe einbaut. Die Portnummer kommt nun wahlweise aus der Umgebungsvariablen PORT. Für das Kodieren der HTML-Ausgabe mit der Funktion htmlEncode() ist bereits ein Node.js-Erweiterungsmodul mit Namen htmlencode notwendig. Eine entsprechende Funktion gibt es im Kern von Node.js leider nicht, was Nutzer in der Microsoft-Welt verwundern wird, denn dort gibt es eine entsprechende Funktion seit den Zeiten von Active Server Pages Mitte der neunziger Jahre. Listing 3.3 zeigt eine Rückgabe im JSON-Format.

var http = require('http');
var url = require('url');
var htmlencode = require('htmlencode');
var port = process.env.PORT || 3000;

var anfrageBearbeiter = function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
// Auswertung des Querystring
var parsed_url = url.parse(req.url, true)
// HTML-Ausgabe
res.write("<html><body>");
res.write("<h1>Hallo Welt mit Querystring und HTML-Ausgabe</h1>");

res.write("<h2>Hallo " + htmlencode.htmlEncode(parsed_url.query.name) + "
</h2>");
res.write("</body>");
res.write("</html>");
res.end();
};

var server = http.createServer(anfrageBearbeiter);

server.listen(port, function() {
console.log("Server aufrufen mit http://localhost:" + port +"?name=<Name einsetzen>");
});

Listing 3.2: Erzeugen einer HTML-Ausgabe unter Einbeziehung eines URL-Parameters

var http = require('http');
var url = require('url');

var port = process.env.PORT || 3000;
var anfrageBearbeiter = function(req, res) {
res.writeHead(200, {'Content-Type': 'text/json'});
var parsed_url = url.parse(req.url, true);
var result = {
gruss: "Hallo " + parsed_url.query.name,
name: parsed_url.query.name
};
res.end(JSON.stringify(result));
};
var server = http.c...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang