Node.JS

Die MSXFAQ ist keine Developer-Seite aber auch Consultants könnten mit Node.JS in Kontakt kommen. Als ich bei Node.JS als "JavaScript" auf dem Server gelesen habe, war ich erst mal nicht elektrisiert. Wie ich dann aber auf Wikipedia (http://de.wikipedia.org/wiki/NodeJS) die beiden Beispiele gesehen haben, wie man einen einfachen Webserver/WebService und insbesondere einen TCP-Service bauen kann, war meine Neugierde geweckt, z.B. für UDP/TCP-Probes. Dies ist mein Spickzettel und Linksammlung zu Node.JS.

Node.JS und JavaScript

Mein Weg zu Node.JS war nicht geradlinig. Auf der einen Seite habe ich immer wieder Beispielcode gesehen, mit der mal eben schnell ein Webserver bereitgestellt wurde. Einige nutzen die HTTP Methode, andere einfach Express.js. Für mich sah das alles erst mal aus, wie eine JavaScript-Datei, die je nach URL bestimmte Funktionen aufgerufen hat.

Auf der anderen Seite gibt es von Microsoft eine riesige Anzahl von Bibliotheken, die alle auf JavaScript aufsetzen und durch Entwickler im Client integriert werden können, z.B. Angular, React, Graph Toolkit oder auch Authentication Libraries. u.a. Es handelt sich um Code, denn ich in einer HTML-Seite einbinden und nutzen kann. Die MSAL-Library gibt es sogar für Clients und die Integration auf dem Server. Interessant fand ich dabei, für welche Plattformen es entsprechende Module/Packages in den passenden Sprachen gibt. Neben "MSAL.NET" gibt es die Library auch für Java (MSA4J) aber auch für JavaScript und JavaScript-Frameworks. msal-angular. msal-browser, msal-core, msal-react, msal-node.

JavaScript ist also mitnichten nur eine Sprache für das Web, welche im Browser ausgeführt wird. Dort ist JavaScript natürlich die einzig verbliebene Programmiersprache, nachdem dem Adobe Flash aber auch Microsoft Silverlight und andere AddOns verschwunden sind. Node.js gibt es für quasi jede Plattform (Windows, Linux, Mac, Docker, AIX, ) und damit auch RaspberryPi und Co.

Aber Code kann auch im Backend laufen und so steht geschrieben, dass ich erst einen Browser starten muss, der dann eine HTML-Datei lädt, die ihrerseits JavaScript-Code ausführt um die Ausgaben ins HTML-Dokument einfügt? Wenn sehr viele Programmierer wissen, wie sie JavaScript codieren, dann könnte man damit doch auch Serverprogramme oder Skripte erstellen, die auf dem Backend laufen, Aufgaben automatisieren oder ihre Ergebnisse z.B. an einen Client übermitteln.

Und genau das macht Node.JS: Node.JS ist eine JavaScript-Laufzeitumgebung, um JavaScript auszuführen. Genau wie "PowerShell.exe" oder VBSCRIPT.EXE entsprechende Umgebungen für PS1-Dateien oder VBS-Skripte ist. Durch Node.JS ausgeführte Skripte laufen aber nicht im Browser und haben daher erst einmal keine "GUI", d.h. es gibt kein HTML-DOM-Objekt. Node.js kann aber natürlich Ausgaben auf STSDOUT schreiben, Informationen von STDIN lesen und sehr einfach auf HTTP-Anfragen eines Browsers antworten.

Aber Node.JS funktioniert aus meiner Sicht gänzlich anders als klassische Programme. Ein PowerShell-Script, VBScript o.ä. werden in der Regel "sequentiell" ausgeführt. Man kann sie zwar mit Funktionen und Prozeduren "gliedern" oder objektorientiert arbeiten aber ohne besondere Kniffe laufen die Arbeiten sequentiell ab. Und sobald etwas auf eine "Eingabe" wartet, wartet das Programm eben. Man muss schon manuell anfangen, Dinge zu parallelisieren (Siehe PS Parallel). Wer ein Windows Programm schreibt, kann es auch sequentiell schreiben aber sobald man eine GUI braut, werden Sie die "Eventhandler" kennenlernen. Der Druck auf einen Button einer GUI wird nicht vom "Hauptprogramm" permanent abgefragt, sondern Windows startet eine hinter dem Button hinterlegte Funktion.

Node.JS ist das ganze nun in JavaScript und ohne GUI und Buttons. Es ist aber "Eventorientiert", d.h. wenn etwas "passiert" (z.B. eine neue TCP-Verbindung, eine neue WebAnfrage, ein neuer Signalstatus auf einem PIN o.ä.), dann wird die dazu gehörige Funktion gestartet. Es gibt also keine ""blockierendes" Warten oder Pollen auf Eingaben sondern man stellt eine CallBack-Funktion bereit, die vom Framework dann gestartet wird. Und das ganze basierend auf "JavaScript".

NNode.JS installieren

Node.JS gehört zumindest auf meinem Windows PC nicht zur Standardinstallation und muss daher erst heruntergeladen und installiert werden. Vielleicht ist es aber schon installiert oder einer älteren Version. Dann sollten Sie kurz die Version von NPM und NODE prüfen. Die Befehle funktionieren auf Windows und Linux.

node -v
npm -v

Node.JS gibt es natürlich auch für Linux, MacOS, als Docker Container. Wenn Node.JS noch nicht installiert ist, dann können Sie es manuell oder über einen Paketmanager installiere.

Download
https://nodejs.org/en/download/

Die Node.JS gehört auch der Package Manager "NPM", über den sehr einfach Module nach installiert werden können. Das ist etwas mit PSGallery und den PowerShell Modulen ähnlich. Die Installation auf einen RasPi ist natürlich nicht nur ein "Doppelklick" auf eine MSI oder EXE, sondern kann über den Package Manager erfolgen.

sudo apt-get update
sudo apt-get dist-upgrade
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs
node -v

Für die Hardcore-Entwickler geht es auch manuell

# Download der aktuellen Distribution als Source. Eventuell aktuelleren Pfad anpassen
wget http://nodejs.org/dist/latest/node-v17.5.0.tar.gz

# Auspacken der Quellen
tar xfvz node-v17.5.0.tar.gz

# Quellen kompilieren. Das kann auf dem RasPi bis zu 2h dauern
cd node-v17.5.0/
./configure
# wenn der "make" mit "nohub" gestartet wird, dann läuft der make weiter, wenn die SSH-Verbindung abbricht
# mit dem Tail kann dann geprueft werden, wann der make fertig ist.
#nohup make >> build.log &
#tail -f build.log

# und dann muss das Paket noch installiert werden
sudo make install

## sofern noch nicht installiert muss GIT für den Paketmanager installiert werden
sudo apt-get install git

Mein kleiner Webserver

Node.JS ist keine Erweiterung eines bestehenden Webservers wie Apache, NGIX oder IIS, der je nach Extension dann den entsprechenden Interpreter oder Shell eingebunden hat. Node.JS spielt seine Stärke aus, wenn er selbst auf einem TCP-Port lauscht und die Anfragen dann über einen Scheduler direkt an die JavaScript-Engine durchreicht. Das Ziel der Entwickler war ein hoch performanter Webservice für Automatisierungsaufgaben, REST-Apis etc.

As an asynchronous event-driven JavaScript runtime, Node.js is designed to build scalable network applications.
...Upon each connection, the callback is fired, but if there is no work to be done, Node.js will sleep.
https://nodejs.org/en/about/

mkdir /home/pi/node-scripts
code /home/pi/node-scripts/main.js

In die Main.js lege ich folgenden Code ab:

console.log("Import Module http");
const http = require('http');

console.log("Start HTTP-Server");
const webserver3000 = http.createServer(function (req, res) {
  let today = Date ()
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.write('MSXFAQ Server'+ today);
  res.end();
}).listen(3000);

console.log("Wait for Requests");

Allerdings ist es auch in PowerShell sehr einfach, so einen einfachen HTTP-P-Listener aufzubauen. Sie eignen sich sehr gut für Webservices, die über einen Port einen bestimmten Aufruf verarbeiten und ein Ergebnis liefern müssen.

Einen kompletten Webserver könnten Sie zwar auch darauf aufbauen aber bei jedem Request wird der Code neu aufgerufen und wenn Sie mehrere URLs bedienen wollen, dann müssen Sie dies in die Funktion einbauen. Dann lohnt sich vielleicht der Blick auf das "Express"-Addon, welches über NPM einfach installiert werden kann. Es macht einige Dinge einfacher.

Es instanziert den Webserver und ich kann dann für jede eigene URL eigenen Code addieren und entfernen. Ein einfacher Webserver mit der Express-Komponente wäre dann.

const express = require("express");
const app = express();
app.get("/", (request,response) => { response.send("done");});
app.listen(3000, () => {console.log("Webserver auf Port 3000 gestartet")})

Bei mir musste ich noch die Rückfrage der lokalen Firewall abnicken, ehe ich per HTTP den Service erreichen konnte.

Das ähnelt etwa dem Projekt "Polaris" für PowerShell, welches ebenfalls einen Service im Hintergrund startet und je nach URL

TCP EchoServer

Mit wenigen Zeilen ist auch ein einfacher TCP-Echo-Server programmiert. 

var net = require('net');
 
var server = net.createServer(function (socket) {
  socket.setEncoding("utf8");
 
  socket.on('data', function (data) {
    socket.send(data);
  });
 
});
 
server.listen(50000, 'localhost');

Der gleiche Code läuft unverändert auf Windows, Linux, Apple, RasPi u.a.

Hintergrund, Parallel, Sequentiell

Wenn Sie das Webserver-Beispiel mit "http" oder "express" in der node.js-Konsole gestartet haben dann blinkt sie weiter ein Cursor an. Der Webserver läuft quasi im Hintergrund und ein Zugriff per Browser liefert auch Ergebnisse aus, während ich in der node-Laufzeitumgebung weitere Aktionen auslösen kann. JavaScript ist ja eigentlich "Single Threaded" aber node.js pflegt einen Stack, auf dem die abzuarbeitenden Aufgaben abgelegt werden. Die Ausführung selbst ist dann zwar sequentiell, aber Funktionen arbeiten in der Regel mit Callback-Funktionen. Wenn ich eine Funktion aufrufe, gebe ich ihr auch den Code mit, den Sie mit der Rückgabe ausführt. Mein klassisches "Hauptprogramm" wartet also nicht auf die Rückkehr sondern kann direkt weitermachen. Das ist quasi der große Vorteile, dass node.js "non-blocking" ist. Der Versuch das System zu überlisten, indem ich eine "Warteschleife" einbaue, geht nicht, da es in JavaScript diese Funktion "sleep" o.ä. nicht gibt.

Wenn ich also eine Aktion "warten" lassen will, dann muss ich mit einem Timer-Event arbeiten, der den Code dann wieder ausführt. Wenn ich die NODE-Umgebung aber einfach "beschäftige", indem ich den folgenden Code ausführe, dann kommt der immer noch im Hintergrund auf Anfragen wartende Webserver erst wieder zum Zuge, wenn der Code beendet wurde.

let startdate = Date.now();
do {
} while (Date.now() - startdate < 10000);

Solche "Sleep"-Funktion" sind aber sehr unhöflich, da sie andere Callback-Ergebnisse an der Verarbeitung hindern und auch die CPU stressen. Auf einem Quad-Core ist ein Kern ausgelastet.

Lösen Sie sich von "sleep", wenn ihr Code eine bestimmte Zeit warten soll. Fragen sie sich eher, warum sie aktiv warten wollen. Es ist allemal besser die Kontrolle abzugeben oder eine Funktion aufzurufen, die nach Erfolg oder Timeout dann die Verarbeitung startet.

Wer wirklich einen Sleep braucht, sollte einen Timer nutzen, der nach Ablauf der Zeit die gewünschte Logik ausführt. Das eigentlich aufrufende Programm kann sich dann beendet.

Auch umgekehrt sollten Sie den ausgeführten Code optimieren. Folgendes Beispiel zeigt, dass ich absichtlich einen Request sehr lange dauern lassen kann.

const http = require('http');
const webserver3007 = http.createServer(function (req, res) {
  let today = Date ()
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.write('MSXFAQ Server'+ today);
  let startdate = Date.now();
  do {
  } while (Date.now() - startdate < 10000);
  res.write('MSXFAQ Server'+ today);
  res.end();
}).listen(3007);

Wenn ich nun auf http://localhost:3007 gehe, dann wartet mein Browser ca.10 Sekunden aber in der Zeit ist auch meine node.js-Konsole "angehalten". Nicht einmal Tastatur-Eingaben werden bedient. Bei einem "Multi-Threading-System" können mehrere Prozesse parallel ablaufen, weil die CPU jedem Prozess immer mal wieder Zeit zukommen lässt. Was gut klingt habt aber auch einen Overhead.

Die gleiche Herausforderung haben sie auch bei JavaScript im Browser. Lang laufende Aktionen wie z.B. der Abruf von größeren Daten sollten nicht die anderen Prozesse blockieren, indem Sie aktiv darauf warten, sondern über Callback-Funktionen fortgesetzt werden. In dem Zuge sollten Sie sich dann über den Scope von Variablen informieren. Dennoch: In JavaScript gibt es eigentlich keine "Pause", was für viele Admins und Consultants mit einem Background von sequentiellen Skriptsprachen wie Shell, VBScript oder PowerShell erst einmal ungewohnt ist.

HTTP-Anfragen

Bei meinem ersten "Projekt" wollte ich direkt von einem REST-Service eine Information abholen und weiter verarbeiten. Mit JavaScript im Browser ist das ganz einfach und es ist schon eher die Qual der Wahl. Die meisten Projekte nutzen aber wohl XMLHttpRequest.

Node.js ist aber nicht der Browser und auch wenn die gleiche JavaScript Engine darunter genutzt wird, so fehlt Node.JS das Browser-Window und davon abgeleitete klasse. Aber auch hier gibt es eine Lösung über das NPM-Modul XMLHttpRequest.

Node.js und PowerShell

Auf der Seite ADSync Webservice habe ich ein Sample vorgestellt, wie ich per HTTP einfache Powershell-Abfragen starten und die Ergebnisse bekommen kann. Node.js ist ja die Java-Version aber kommt natürlich nicht direkt an die gleichen Daten. Allerdings kann Node.JS, anders als JavaScript in der Sandbox des Browsers, auch externe Programme aufrufen. Damit kann ich aus Node.JS auch PowerShell starten.

// Code inspiriert von https://nodejs.org/api/child_process.html

// Nutze das Modul child_process um ein PowerShell Skript zu starten
var spawn = require("child_process").spawn,child;
child = spawn("powershell.exe",["c:\\nodejs\\sscript1.ps1"]);

// Ausgabe der Ausgaben von STDOUT
child.stdout.on("data",function(data){
    console.log("Powershell Data: " + data);
});

// Ausgabe der Ausgaben von STDERR
child.stderr.on("data",function(data){
    console.log("Powershell Errors: " + data);
});

// Ausgabe, wenn die Funktion beendet wurde
child.on("exit",function(){
    console.log("Powershell Script finished");
});
child.stdin.end(); //end input

Ich habe ein paar Tests damit gemacht aber bald wieder verworfen. Technisch startet Node.JS damit jedes mal einen eigenen PowerShell-Prozess, in dem ich dann wieder eine Exchange Remote Powershell starten oder andere Module nachladen müsste. Auch wenn der Prozess dann "im Hintergrund" läuft und die eigentliche Ausführung von note.JS nicht weiter blockiert, dauert der Aufruf seölst doch eher lange und ist teuer. Ich habe hier nicht mehr Zeit investiert.

PowerShell und JavaScript

Also wir Software für Windows Desktops entwickelt haben, haben wir und natürlich verschiedener Bibliotheken bedient, die als DLLs dahergekommen sind. Wer Programme für den Browser entwickelt, bedient sich auch verschiedener Bibliotheken, die aber als JavaScript-Klassen daher kommen. Auch Microsoft stellt sehr viele interessante derartige Module bereit, die nicht alle eine "GUI" benötigen. Da habe ich mich natürlich gefragt, ob ich nicht aus meinen PowerShell-Skripts mittels Node.js mich dieser Klassen bedienen kann.

Ich kann node.exe selbst mit Parametern und letztlich dem Skript starten aber auch Parameter an das Skript übergeben:


Quelle: https://nodejs.org/api/cli.html

Ein in Node.JS gestartetes JavaScript kann auch von STDIN lesen und damit von PowerShell mit Daten "gefüllt" werden.

Aktuell kann ich zu dieser Nutzung noch keine verlässliche Informationen beisteuern. Sobald ich Node.js mit meinem Powershell-Skripten intensiver genutzt habe, werde ich die Seite erweitern.

Weitere Links

CRE167 node.js - Die event-basierte Programmierumgebung für das Realtime Web
http://cre.fm/cre167-nodejs

Node JS Tutorial Deutsch für Anfänger
https://www.youtube.com/watch?v=SSCeKhoY8Kk