… jeder von euch kennt das Problem, der Kollege kommt um die Ecke und möchte wenn möglich sofort, dass ein neues geiles Tools mit in den Infra Stack aufgenommen wird, gesagt getan.

In diesem Bespiel eine besonders cooles in NodeJS geschriebenes Dashboard für… lass es hier ein DockerManagement Tool sein. Dabei stößt man bei der großen Akzeptanz durch die Kollegen, auch auf einmal auf noch ganz andere Probleme (Stichwort: Performance Bottlenecks wie DDoS durch eigene Leute).

Hierbei könnte euch das NodeJS Modul Cluster behilflich sein, hier ist die Doku zu finden.

Cluster macht dabei nicht viel, es erweitert den NodeJS Stack insbesondere das Event Driven Modell von NodeJS ( so Arbeitet Google(s) v8 Engine ) um eine Art Interprocess Schnittstelle zu forked Child NodeJS Prozessen, die sich dabei Filesystem Elemente/Typen wie Sockets, Filedeskriptoren etc. teilen können und somit gemeinsam auf diesen operieren können.

Hier wollen wir eine schon bestehende Express Framework Anwendung mit einigen wenigen Zeilen Code um die Fähigkeit erweitern, dem System zur Verfügung stehenden CPU Cores effizienter ausnutzen zu können, damit sich die Events schneller abarbeiten lassen.

Ich werde hier allerdings nicht Express selber beschreiben, sondern setzte hier voraus das der Leser dieses Framework kennt. Wenn nicht könnt ihr die Infos unter diesem Link abrufen, so let’s beginn …

$ sudo -i                                  # <- da meine Workstation ein Ubuntu ist sollten wir zumindest was die Essentials sind kurz zu 'root' werden um diese Problemlos installiert zu bekommen
$ npm install express -g                   # <- installiert uns das Express Framework samt CLI Tools
express@4.13.3 /usr/lib/node_modules/express
├── escape-html@1.0.2
├── merge-descriptors@1.0.0
├── cookie@0.1.3
├── array-flatten@1.1.1
├── cookie-signature@1.0.6
├── utils-merge@1.0.0
├── content-type@1.0.1
├── fresh@0.3.0
├── path-to-regexp@0.1.7
├── content-disposition@0.5.0
├── vary@1.0.1
├── etag@1.7.0
├── serve-static@1.10.0
├── range-parser@1.0.2
├── methods@1.1.1
├── parseurl@1.3.0
├── depd@1.0.1
├── qs@4.0.0
├── on-finished@2.3.0 (ee-first@1.1.1)
├── finalhandler@0.4.0 (unpipe@1.0.0)
├── debug@2.2.0 (ms@0.7.1)
├── proxy-addr@1.0.8 (forwarded@0.1.0, ipaddr.js@1.0.1)
├── send@0.13.0 (destroy@1.0.3, statuses@1.2.1, ms@0.7.1, mime@1.3.4, http-errors@1.3.1)
├── type-is@1.6.9 (media-typer@0.3.0, mime-types@2.1.7)
└── accepts@1.2.13 (negotiator@0.5.3, mime-types@2.1.7)
$ npm install express-generator -g
/usr/bin/express -> /usr/lib/node_modules/express-generator/bin/express
express-generator@4.13.1 /usr/lib/node_modules/express-generator
├── sorted-object@1.0.0
├── commander@2.7.1 (graceful-readlink@1.0.1)
└── mkdirp@0.5.1 (minimist@0.0.8)
$ exit                                     # <- ab hier geht es ohne 'root' Rechte weiter
$ mkdir cool-app && cd cool-app            # <- Projekt Verzeichnis anlegen und in dieses wechseln
$ pwd                                      # <- vergewissern das wir uns auch in diesem wirklich befinden
/home/enzo/nodejsProjects/cool-app
$ express --git .                          # <- wir provisionieren uns unser Projekt from Scratch, dieses ist somit direkt Lauffähig ohne das wir einen Zeile Code schreiben müssen, haben hier leider keine Zeit zu ;)
   create : .
   create : ./package.json
   create : ./app.js
   create : ./.gitignore
   create : ./public
   create : ./routes
   create : ./routes/index.js
   create : ./routes/users.js
   create : ./views
   create : ./views/index.jade
   create : ./views/layout.jade
   create : ./views/error.jade
   create : ./bin
   create : ./bin/www
   create : ./public/javascripts
   create : ./public/images
   create : ./public/stylesheets
   create : ./public/stylesheets/style.css
 
   install dependencies:
     $ cd . && npm install
 
   run the app:
     $ DEBUG=cool-app:* npm start
 
$

Ab hier können wir das ganze mal kurz testen, um uns zu vergewissern das die Anwendung auch läuft …

$ npm start
 
> cool-app@0.0.0 start /home/enzo/nodejsProjects/cool-app
> node ./bin/www

Nicht wundern die Applikation bleibt im Vordergrund hängen, was ist Ok ist, somit können wir diese schneller terminieren und relaunchen. Weiter geht es mit einem Test im Browser, die Anwendung lauscht standardmäßig am Port 3000, somit rufen wir hier einmal http://localhost:3000 auf und lassen uns überraschen was da so schönes kommt …

Geil es läuft also, nun gut machen wir das ganze mit Cluster noch skalierbarer, let’s go …

Öffnet hierzu im Projekt Verzeichnis einmal die bin/www Datei, diese stellt laut package.json unseren Eintrittspunkt in die Anwendung dar, wir kopieren uns diese als Backup weg bevor wir anfangen hier Änderungen vorzunehmen.

Kommentiert bitte folgende Zeilen einmal aus …

/**
 * Get port from environment and store in Express.
 */
 
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
 
/**
 * Create HTTP server.
 */
 
var server = http.createServer(app);
 
/**
 * Listen on provided port, on all network interfaces.
 */
 
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

… nun fügt ihr ab Zeile 10 bitte folgendes hinzu …

 
// cluster requirements
var cluster = require('cluster');
var numCPUs = require('os').cpus().length;
 
if( cluster.isMaster ) {
  for( var i = 0; i < numCPUs; i++ ) {
    cluster.fork();
  }
 
  cluster.on( 'listening', function( worker ){
    console.log( 'worker ' + worker.process.pid + ' is now listen for incoming connections ... ' );
  });
 
  cluster.on( 'exit', function( worker, code, signal ) {
    console.log( 'worker ' + worker.process.pid + ' died' );
  });
} else {
    // Workers can share any TCP connection
    // In this case it is an HTTP server
 
    /**
    * Get port from environment and store in Express.
    */
 
    var port = normalizePort(process.env.PORT || '3000');
    app.set('port', port);
 
    /**
    * Create HTTP server.
    */
 
    var server = http.createServer(app);
 
    /**
    * Listen on provided port, on all network interfaces.
    */
 
    server.listen(port);
    server.on('error', onError);
    server.on('listening', onListening);
}

.. jetzt sollte das ganze auch schon funktionieren, probieren wir es doch einmal aus.

$ npm start
 
> cool-app@0.0.0 start /home/enzo/nodejsProjects/cool-app
> node ./bin/www
 
worker 31979 is now listen for incoming connections ... 
worker 31974 is now listen for incoming connections ... 
worker 31985 is now listen for incoming connections ... 
worker 31996 is now listen for incoming connections ... 
GET / 304 840.784 ms - -
GET /stylesheets/style.css 304 31.416 ms - -

Jetzt könnte man denken das sich hier nichts geändert hat, dem ist aber nicht so, der Beweis ist in der Prozess Tabelle zu finden, gucken wir doch kurz einmal hinein …

$ ps auxf | grep node
enzo     31969  0.1  0.4 901264 33004 pts/7    Sl+  14:47   0:00          |   |       \_ node ./bin/www
enzo     31974  0.1  0.4 901256 32324 pts/7    Sl+  14:47   0:00          |   |           \_ /usr/bin/nodejs /home/enzo/nodejsProjects/cool-app/bin/www
enzo     31979  0.4  0.6 946192 47560 pts/7    Sl+  14:47   0:01          |   |           \_ /usr/bin/nodejs /home/enzo/nodejsProjects/cool-app/bin/www
enzo     31985  0.1  0.4 902668 32736 pts/7    Sl+  14:47   0:00          |   |           \_ /usr/bin/nodejs /home/enzo/nodejsProjects/cool-app/bin/www
enzo     31996  0.1  0.4 901256 32168 pts/7    Sl+  14:47   0:00          |   |           \_ /usr/bin/nodejs /home/enzo/nodejsProjects/cool-app/bin/www

Whoop whoop, 4 neue NodeJS Prozesse die hier ihre Arbeit verrichten, somit lassen sich auch die beschäftigsten Web Anwendungen wieder beschleunigen.

Das ist aber nur der Anfang wie sich NodeJS Applications aller Art verbessern lassen, für weiter Themen habe ich heute allerdings keine Zeit daher heben wir uns den Rest einfach für ein anderes mal auf.

Ich wünsche euch hiermit noch viel Spaß, ich hoffe ihr habt nun mehr Lust auf NodeJS bekommen.

Enrico Labedzki

Autor: Enrico Labedzki

Enrico ist beruflich ganz schön rumgekommen – IT hat ihn aber immer beschäftigt. Nach einem Ausflug in die Selbstständigkeit im Bereich Webentwicklung und Network Solutions, wurde es dann Zeit Nägel mit Köpfen zu machen und endlich den Weg als Softwareentwickler und Systemintegrator einzuschlagen. In seiner Freizeit widmet sich der passionierte Bastler der Elektrotechnik und Animatronik. Bei Netways bereichert er mit seinem vielseitigen Know-How das Managed Service-Team.