So entwickeln Sie eine Node.js-TCP-Server-Anwendung mit PM2 und Nginx unter Ubuntu 16.04

Der Autor hat OSMI ausgewählt, um eine Spende als Teil des Write for DOnations zu erhalten Programm.

Einführung

https://nodejs.org [Node.js] ist eine beliebte Open-Source-JavaScript-Laufzeitumgebung, die auf der V8-JavaScript-Engine von Chrome basiert. Node.js wird zum Erstellen von serverseitigen und Netzwerkanwendungen verwendet. _TCP (Transmission Control Protocol) _ ist ein Netzwerkprotokoll, das eine zuverlässige, geordnete und fehlerüberprüfte Übermittlung eines Datenstroms zwischen Anwendungen ermöglicht. Ein TCP-Server kann eine TCP-Verbindungsanforderung annehmen, und sobald die Verbindung hergestellt ist, können beide Seiten Datenströme austauschen.

In diesem Lernprogramm erstellen Sie einen grundlegenden Node.js-TCP-Server sowie einen Client zum Testen des Servers. Sie führen Ihren Server als Hintergrundprozess mit einem leistungsstarken Node.js-Prozessmanager namens PM2 aus. Anschließend konfigurieren Sie Nginx als Reverse-Proxy für die TCP-Anwendung und testen die Client-Server-Verbindung von Ihrem lokalen Computer aus.

Voraussetzungen

Um dieses Tutorial abzuschließen, benötigen Sie:

Schritt 1 - Erstellen einer Node.js-TCP-Anwendung

Wir werden eine Node.js-Anwendung mit TCP-Sockets schreiben. Dies ist eine Beispielanwendung, die Ihnen hilft, die Bibliothek Net in Node.js zu verstehen, mit der wir unformatierte TCP-Server- und -Client-Anwendungen erstellen können.

Erstellen Sie zunächst ein Verzeichnis auf Ihrem Server, in dem Sie Ihre Node.js-Anwendung platzieren möchten. In diesem Tutorial erstellen wir unsere Anwendung im Verzeichnis "+ ~ / tcp-node js-app":

mkdir ~/tcp-nodejs-app

Wechseln Sie dann in das neue Verzeichnis:

cd ~/tcp-nodejs-app

Erstellen Sie eine neue Datei mit dem Namen "+ package.json" für Ihr Projekt. Diese Datei listet die Pakete auf, von denen die Anwendung abhängt. Durch das Erstellen dieser Datei wird der Build reproduzierbar, da diese Liste der Abhängigkeiten einfacher für andere Entwickler freigegeben werden kann:

nano package.json

Sie können die Datei "+ package.json " auch mit dem Befehl " npm init +" generieren, mit dem Sie nach den Details der Anwendung gefragt werden. Die Datei muss jedoch noch manuell geändert werden, um zusätzliche Elemente, einschließlich eines Startvorgangs, hinzuzufügen Befehl. Daher erstellen wir die Datei in diesem Lernprogramm manuell.

Fügen Sie der Datei den folgenden JSON-Code hinzu, der den Namen, die Version, die Hauptdatei, den Befehl zum Starten der Anwendung und die Softwarelizenz der Anwendung angibt:

package.json

{
 "name": "tcp-nodejs-app",
 "version": "1.0.0",
 "main": "server.js",
 "scripts": {
   "start": "node server.js"
 },
 "license": "MIT"
}

Im Feld "+ Skripte " können Sie Befehle für Ihre Anwendung definieren. Mit der hier angegebenen Einstellung können Sie die App ausführen, indem Sie " npm start " anstelle von " node server.js +" ausführen.

Die "+ package.json +" - Datei kann auch eine Liste der Laufzeit- und Entwicklungsabhängigkeiten enthalten, wir haben jedoch keine Abhängigkeiten von Drittanbietern für diese Anwendung.

Nachdem Sie das Projektverzeichnis und "+ package.json" eingerichtet haben, erstellen wir den Server.

Erstellen Sie in Ihrem Anwendungsverzeichnis eine Datei + server.js +:

nano server.js

Node.js bietet ein Modul mit dem Namen "+ net ", das die TCP-Server- und Client-Kommunikation ermöglicht. Laden Sie das ` net ` -Modul mit ` require () +` und definieren Sie dann Variablen, die den Port und den Host für den Server enthalten:

server.js

const net = require('net');
const port = 7070;
const host = '127.0.0.1';

Für diese App wird der Port "+ 7070 " verwendet. Sie können jedoch jeden verfügbaren Port verwenden, den Sie möchten. Wir verwenden " 127.0.0.1 " für den " HOST +", wodurch sichergestellt wird, dass unser Server nur unsere lokale Netzwerkschnittstelle überwacht. Später werden wir Nginx als Reverse Proxy vor diese App stellen. Nginx ist mit der Handhabung mehrerer Verbindungen und der horizontalen Skalierung bestens vertraut.

Fügen Sie dann diesen Code hinzu, um einen TCP-Server mit der Funktion "+ createServer () " aus dem Modul " net " zu erzeugen. Beginnen Sie dann, auf dem von Ihnen definierten Port und Host mit der Funktion ` listen () ` des Moduls ` net +` nach Verbindungen zu suchen:

server.js

...
const server = net.createServer();
server.listen(port, host, () => {
   console.log('TCP Server is running on port ' + port +'.');
});

Speichern Sie + server.js + und starten Sie den Server:

npm start

Sie sehen diese Ausgabe:

OutputTCP Server is running on port 7070

Der TCP Server läuft auf Port + 7070 +. Drücken Sie "+ STRG + C +", um den Server zu stoppen.

Nachdem wir wissen, dass der Server empfangsbereit ist, schreiben wir den Code für die Clientverbindungen.

Wenn ein Client eine Verbindung zum Server herstellt, löst der Server ein "+ connection " - Ereignis aus, das wir beobachten werden. Wir definieren ein Array verbundener Clients, die wir " sockets +" nennen, und fügen jede Client-Instanz zu diesem Array hinzu, wenn der Client eine Verbindung herstellt.

Wir verwenden das Ereignis "+ data ", um den Datenstrom von den verbundenen Clients zu verarbeiten, und verwenden das Array " sockets +", um Daten an alle verbundenen Clients zu senden.

Fügen Sie diesen Code zur Datei "+ server.js +" hinzu, um die folgenden Funktionen zu implementieren:

server.js

...

let sockets = [];

server.on('connection', function(sock) {
   console.log('CONNECTED: ' + sock.remoteAddress + ':' + sock.remotePort);
   sockets.push(sock);

   sock.on('data', function(data) {
       console.log('DATA ' + sock.remoteAddress + ': ' + data);
       // Write the data back to all the connected, the client will receive it as data from the server
       sockets.forEach(function(sock, index, array) {
           sock.write(sock.remoteAddress + ':' + sock.remotePort + " said " + data + '\n');
       });
   });
});

Dadurch wird der Server angewiesen, die von verbundenen Clients gesendeten "+ data " - Ereignisse abzuhören. Wenn die verbundenen Clients Daten an den Server senden, geben wir diese durch Iteration über das Array " sockets +" an alle verbundenen Clients zurück.

Fügen Sie dann einen Handler für "+ close " -Ereignisse hinzu, der ausgelöst wird, wenn ein verbundener Client die Verbindung beendet. Wenn ein Client die Verbindung trennt, möchten wir den Client aus dem Array " sockets +" entfernen, damit wir keine Broadcasts mehr an ihn senden. Fügen Sie diesen Code am Ende des Verbindungsblocks hinzu:

server.js

let sockets = [];
server.on('connection', function(sock) {

   ...

   // Add a 'close' event handler to this instance of socket
   sock.on('close', function(data) {
       let index = sockets.findIndex(function(o) {
           return o.remoteAddress === sock.remoteAddress && o.remotePort === sock.remotePort;
       })
       if (index !== -1) sockets.splice(index, 1);
       console.log('CLOSED: ' + sock.remoteAddress + ' ' + sock.remotePort);
   });
});

Hier ist der vollständige Code für "+ server.js":

server.js

const net = require('net');
const port = 7070;
const host = '127.0.0.1';

const server = net.createServer();
server.listen(port, host, () => {
   console.log('TCP Server is running on port ' + port + '.');
});

let sockets = [];

server.on('connection', function(sock) {
   console.log('CONNECTED: ' + sock.remoteAddress + ':' + sock.remotePort);
   sockets.push(sock);

   sock.on('data', function(data) {
       console.log('DATA ' + sock.remoteAddress + ': ' + data);
       // Write the data back to all the connected, the client will receive it as data from the server
       sockets.forEach(function(sock, index, array) {
           sock.write(sock.remoteAddress + ':' + sock.remotePort + " said " + data + '\n');
       });
   });

   // Add a 'close' event handler to this instance of socket
   sock.on('close', function(data) {
       let index = sockets.findIndex(function(o) {
           return o.remoteAddress === sock.remoteAddress && o.remotePort === sock.remotePort;
       })
       if (index !== -1) sockets.splice(index, 1);
       console.log('CLOSED: ' + sock.remoteAddress + ' ' + sock.remotePort);
   });
});

Speichern Sie die Datei und starten Sie den Server erneut:

npm start

Auf unserem Computer läuft ein voll funktionsfähiger TCP-Server. Als Nächstes schreiben wir einen Client, um eine Verbindung zu unserem Server herzustellen.

Schritt 2 - Erstellen eines Node.js-TCP-Clients

Unser Node.js-TCP-Server wird ausgeführt. Erstellen wir daher einen TCP-Client, um eine Verbindung zum Server herzustellen und den Server zu testen.

Der Node.js-Server, den Sie gerade geschrieben haben, wird noch ausgeführt und blockiert Ihre aktuelle Terminalsitzung. Wir möchten, dass dies bei der Entwicklung des Clients weiterhin ausgeführt wird. Öffnen Sie daher ein neues Terminalfenster oder eine neue Registerkarte. Stellen Sie dann über die neue Registerkarte erneut eine Verbindung zum Server her.

ssh @

Navigieren Sie nach dem Herstellen der Verbindung zum Verzeichnis + tcp-node js-app:

cd tcp-nodejs-app

Erstellen Sie im selben Verzeichnis eine neue Datei mit dem Namen "+ client.js":

nano client.js

Der Client verwendet dieselbe "+ net " - Bibliothek, die in der " server.js" -Datei verwendet wird, um eine Verbindung zum TCP-Server herzustellen. Fügen Sie diesen Code zur Datei hinzu, um eine Verbindung zum Server über die IP-Adresse "+ 127.0.0.1 " an Port " 7070 +" herzustellen:

client.js

const net = require('net');
const client = new net.Socket();
const port = 7070;
const host = '127.0.0.1';

client.connect(port, host, function() {
   console.log('Connected');
   client.write("Hello From Client " + client.address().address);
});

Dieser Code versucht zunächst, eine Verbindung zum TCP-Server herzustellen, um sicherzustellen, dass der von uns erstellte Server ausgeführt wird. Sobald die Verbindung hergestellt ist, sendet der Client mit der Funktion "+ client.write a" die Adresse "" Hello From Client " client.address (). Address" an den Server. Unser Server empfängt diese Daten und sendet sie an den Client zurück.

Sobald der Client die Daten vom Server zurückerhält, soll die Antwort des Servers gedruckt werden. Fügen Sie diesen Code hinzu, um das "+ data +" - Ereignis abzufangen und die Antwort des Servers auf die Befehlszeile zu drucken:

client.js

client.on('data', function(data) {
   console.log('Server Says : ' + data);
});

Behandeln Sie die Trennung vom Server ordnungsgemäß, indem Sie den folgenden Code hinzufügen:

client.js

client.on('close', function() {
   console.log('Connection closed');
});

Speichern Sie die Datei + client.js +.

Führen Sie den folgenden Befehl aus, um den Client zu starten:

node client.js

Die Verbindung wird hergestellt und der Server empfängt die Daten und sendet sie an den Client zurück:

client.js OutputConnected
Server Says : 127.0.0.1:34548 said Hello From Client 127.0.0.1

Wechseln Sie zurück zu dem Terminal, auf dem der Server ausgeführt wird. Die folgende Ausgabe wird angezeigt:

server.js OutputCONNECTED: 127.0.0.1:34550
DATA 127.0.0.1: Hello From Client 127.0.0.1

Sie haben überprüft, ob Sie eine TCP-Verbindung zwischen Ihrem Server und den Client-Apps herstellen können.

Drücken Sie "+ STRG + C ", um den Server zu stoppen. Wechseln Sie dann zu der anderen Terminalsitzung und drücken Sie " STRG + C +", um den Client zu stoppen. Sie können diese Terminalsitzung jetzt von Ihrem Server trennen und zu Ihrer ursprünglichen Terminalsitzung zurückkehren.

Im nächsten Schritt starten wir den Server mit PM2 und führen ihn im Hintergrund aus.

Schritt 3 - Server mit PM2 ausführen

Sie haben einen funktionierenden Server, der Clientverbindungen akzeptiert, der jedoch im Vordergrund ausgeführt wird. Lassen Sie uns den Server mit PM2 ausführen, damit er im Hintergrund ausgeführt und ordnungsgemäß neu gestartet werden kann.

Installieren Sie zunächst PM2 mit + npm + global auf Ihrem Server:

sudo npm install pm2 -g

Sobald PM2 installiert ist, können Sie damit Ihren Server ausführen. Anstatt "+ npm start " auszuführen, um den Server zu starten, verwenden Sie den Befehl " pm2 +". Starten Sie den Server:

pm2 start server.js

Die Ausgabe sieht folgendermaßen aus:

[secondary_label Output
[PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting /home/sammy/tcp-nodejs-app/server.js in fork_mode (1 instance)
[PM2] Done.
┌────────┬──────┬────────┬───┬─────┬───────────┐
│ Name   │ mode │ status │ ↺ │ cpu │ memory    │
├────────┼──────┼────────┼───┼─────┼───────────┤
│ server │ fork │ online │ 0 │ 5%  │ 24.8 MB   │
└────────┴──────┴────────┴───┴─────┴───────────┘
Use `pm2 show <id|name>` to get more details about an app

Der Server läuft jetzt im Hintergrund. Wenn wir den Computer jedoch neu starten, wird er nicht mehr ausgeführt. Erstellen wir daher einen systemd-Dienst für ihn.

Führen Sie den folgenden Befehl aus, um die systemd-Startskripts von PM2 zu generieren und zu installieren. Stellen Sie sicher, dass Sie dies mit + sudo + ausführen, damit die systemd-Dateien automatisch installiert werden.

sudo pm2 startup

Sie sehen diese Ausgabe:

Output[PM2] Init System found: systemd
Platform systemd

...

[PM2] Writing init configuration in /etc/systemd/system/pm2-root.service
[PM2] Making script booting at startup...
[PM2] [-] Executing: systemctl enable pm2-root...
Created symlink from /etc/systemd/system/multi-user.target.wants/pm2-root.service to /etc/systemd/system/pm2-root.service.
[PM2] [v] Command successfully executed.
+---------------------------------------+
[PM2] Freeze a process list on reboot via:
$ pm2 save

[PM2] Remove init script via:
$ pm2 unstartup systemd

PM2 wird jetzt als Systemdienst ausgeführt.

Sie können alle Prozesse, die PM2 verwaltet, mit dem Befehl + pm2 list auflisten:

pm2 list

Ihre Bewerbung wird in der Liste mit der ID "+ 0 +" angezeigt:

Output┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬───────┬──────────┐
│ App name │ id │ mode │ pid  │ status │ restart │ uptime │ cpu │ mem       │ user  │ watching │
├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼───────┼──────────┤
│ server   │   │ fork │ 9075 │ online │ 0       │ 4m     │ 0%  │ 30.5 MB   │ sammy │ disabled │
└──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴───────┴──────────┘

In der vorhergehenden Ausgabe werden Sie feststellen, dass "+ gucken +" deaktiviert ist. Diese Funktion lädt den Server neu, wenn Sie eine Änderung an einer der Anwendungsdateien vornehmen. Es ist nützlich für die Entwicklung, aber wir brauchen dieses Feature in der Produktion nicht.

Um weitere Informationen zu einem der laufenden Prozesse zu erhalten, verwenden Sie den Befehl + pm2 show +, gefolgt von seiner ID. In diesem Fall lautet die ID "+ 0 +":

pm2 show

Diese Ausgabe zeigt die Betriebszeit, den Status, die Protokolldateipfade und andere Informationen zur ausgeführten Anwendung an:

OutputDescribing process with id  - name server
┌───────────────────┬──────────────────────────────────────────┐
│ status            │ online                                   │
│ name              │ server                                   │
│ restarts          │ 0                                        │
│ uptime            │ 7m                                       │
│ script path       │ /home/sammy/tcp-nodejs-app/server.js     │
│ script args       │ N/A                                      │
│ error log path    │ /home/sammy/.pm2/logs/server-error-0.log │
│ out log path      │ /home/sammy/.pm2/logs/server-out-0.log   │
│ pid path          │ /home/sammy/.pm2/pids/server-0.pid       │
│ interpreter       │ node                                     │
│ interpreter args  │ N/A                                      │
│ script id         │ 0                                        │
│ exec cwd          │ /home/sammy/tcp-nodejs-app               │
│ exec mode         │ fork_mode                                │
│ node.js version   │ 8.11.2                                   │
│ watch & reload    │ ✘                                        │
│ unstable restarts │ 0                                        │
│ created at        │ 2018-05-30T19:29:45.765Z                 │
└───────────────────┴──────────────────────────────────────────┘
Code metrics value
┌─────────────────┬────────┐
│ Loop delay      │ 1.12ms │
│ Active requests │ 0      │
│ Active handles  │ 3      │
└─────────────────┴────────┘
Add your own code metrics: http://bit.ly/code-metrics
Use `pm2 logs server [--lines 1000]` to display logs
Use `pm2 monit` to monitor CPU and Memory usage server

Wenn der Anwendungsstatus einen Fehler anzeigt, können Sie das Fehlerprotokoll mithilfe des * Fehlerprotokollpfads * öffnen und überprüfen, um den Fehler zu debuggen:

cat /home/tcp/.pm2/logs/server-error-0.log

Wenn Sie Änderungen am Servercode vornehmen, müssen Sie den Anwendungsprozess neu starten, um die Änderungen zu übernehmen.

pm2 restart

PM2 verwaltet jetzt die Anwendung. Jetzt werden wir Nginx verwenden, um Anfragen an den Server zu übermitteln.

Schritt 4 - Richten Sie Nginx als Reverse-Proxy-Server ein

Ihre Anwendung läuft und lauscht auf + 127.0.0.1 +, was bedeutet, dass nur Verbindungen vom lokalen Computer akzeptiert werden. Wir werden Nginx als Reverse-Proxy einrichten, der den eingehenden Datenverkehr verarbeitet und an unseren Server weiterleitet.

Dazu ändern wir die Nginx-Konfiguration so, dass +stream {} + ` und https://nginx.org verwendet werden /de/docs/stream/ngx_stream_proxy_module.html [