Veröffentlichung und Freigabe einer Vorlage in Node.js

0 Aktien
0
0
0
0

Einführung

Das Pub/Sub-Muster ist ein vielseitiges Einweg-Messaging-Muster, bei dem ein Publisher Daten/Nachrichten erzeugt und Subscriber bestimmte Nachrichtentypen abonnieren. Es kann mithilfe einer Peer-to-Peer-Architektur oder eines Message Brokers zur Kommunikationsvermittlung implementiert werden.


Die obige Abbildung zeigt das Peer-to-Peer-Pub/Sub-Modell, bei dem ein Herausgeber Nachrichten direkt an Abonnenten sendet, ohne Zwischenhändler. Abonnenten müssen die Adresse oder den Endpunkt des Herausgebers kennen, um die Nachricht zu empfangen.


Im obigen Bild verwendet das Pub/Sub-Modell einen Message Broker als zentrale Schnittstelle für den Nachrichtenaustausch zwischen Publishern und Subscribern. Der Broker vermittelt den Nachrichtenaustausch und verteilt die Nachrichten von den Publishern an die Subscriber. Subscriber-Knoten abonnieren Nachrichten direkt beim Broker anstatt beim Publisher. Durch den Broker wird die Isolation der Systemknoten verbessert, da sowohl Publisher als auch Subscriber ausschließlich mit dem Broker interagieren. In diesem Tutorial entwickeln Sie eine Echtzeit-Chat-Anwendung, um dieses Muster weiter zu veranschaulichen.

Voraussetzungen
  • Node.js (Version >= 12) ist auf Ihrem Betriebssystem installiert.
  • Ein Code-Editor wie VSCode
  • Redis ist auf Ihrem Rechner installiert.
  • Grundkenntnisse in HTML, DOM, VanillaJS und WebSocket.

Schritt 1 – Serverseitige Implementierung

Um die Serverseite zu starten, initialisieren wir eine einfache Node.js-Anwendung mit dem folgenden Befehl:

npm init -y

Der obige Befehl erstellt eine Standard-package.json-Datei.

Als Nächstes installieren wir das WebSocket (ws)-Abhängigkeitspaket, das während des gesamten Build-Prozesses benötigt wird:

npm install ws

Die serverseitige Implementierung wird eine einfache serverseitige Chat-Anwendung sein. Wir werden folgenden Arbeitsablauf befolgen:

  1. Einen Server einrichten
  2. Lesen Sie die HTML-Datei, um sie im Browser darzustellen.
  3. Richten Sie eine WebSocket-Verbindung ein.
Server-Setup

Erstellen Sie in Ihrem Verzeichnis eine Datei namens app.js und fügen Sie folgenden Code ein:

const http = require("http");
const server = http.createServer((req, res) => {
res.end("Hello Chat App");
});
const PORT = 3459;
server.listen(PORT, () => {
console.log(`Server up and running on port ${PORT}`);
});

Verfahren Server erstellen Im Modul http Inländisch Node.js Es wird zum Starten des Servers verwendet. Der Port, an dem der Server auf Anfragen warten soll, wird festgelegt, und die Listen-Methode der erstellten Serverinstanz wird aufgerufen, um eingehende Anfragen am angegebenen Port zu empfangen.

Befehl node app.js Führen Sie den Befehl in Ihrem Terminal aus und Sie sollten eine Antwort wie diese erhalten:

OutputServer is up and running on port 3459

Wenn Sie diesen Port in Ihrem Browser anfragen, sollten Sie eine Antwort wie diese erhalten:


Lesen Sie die HTML-Datei, um sie im Browser darzustellen.

Erstellen Sie im Hauptordner eine Datei namens index.html und kopieren Sie den folgenden Code hinein:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<p>Serving HTML file</p>
</body>
</html>

Dies ist eine einfache HTML-Datei, die „Hallo“ ausgibt. Nun müssen wir diese Datei lesen und als Antwort ausgeben, sobald eine HTTP-Anfrage an unseren Server gesendet wird.

// app.js
const server = http.createServer((req, res) => {
const htmlFilePath = path.join(__dirname, "index.html");
fs.readFile(htmlFilePath, (err, data) => {
if (err) {
res.writeHead(500);
res.end("Error occured while reading file");
}
res.writeHead(200, { "Content-Type": "text/html" });
res.end(data);
});
});

Hier verwenden wir das interne Pfadmodul und die Funktion verbinden Wir verwenden es, um Pfadsegmente miteinander zu verbinden. Dann verwenden wir die Funktion. Datei lesen Um die Datei zu lesen index.html Wird asynchron verwendet. Benötigt zwei Argumente: den Pfad zur zu lesenden Datei und einen Rücklesebefehl. Statuscode 500 Es wird ein Antwortheader gesendet und eine Fehlermeldung an den Client übermittelt. Wenn die Daten erfolgreich gelesen wurden, wird ein Erfolgsstatuscode zurückgegeben. 200 Wir senden den Antwortheader und die Antwortdaten an den Client, in diesem Fall den Dateiinhalt. Wenn keine Kodierung angegeben ist, z. B. UTF-8, wird der Rohpuffer zurückgegeben. Andernfalls wird die Datei HTML Es wird zurückgegeben.

Senden Sie eine Anfrage an den Server in Ihrem Browser, und Sie sollten Folgendes erhalten:


Einrichten einer WebSocket-Verbindung
// app.js
const webSocketServer = new WebSocket.Server({ server });
webSocketServer.on("connection", (client) => {
console.log("successfully connected to the client");
client.on("message", (streamMessage) => {
console.log("message", streamMessage);
distributeClientMessages(streamMessage);
});
});
const distributeClientMessages = (message) => {
for (const client of webSocketServer.clients) {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
}
};

In der vorherigen Codezeile haben wir einen neuen WebSocket-Server namens erstellt. webSocketServer Wir erstellen es und senden es an den Server. HTTP Wir knüpfen an unsere bestehende Verbindung an. Dadurch können wir sowohl Standard-HTTP-Anfragen als auch WebSocket-Verbindungen über einen einzigen Port 3459 abwickeln.

Das on()-Verbindungsereignis wird ausgelöst, wenn eine erfolgreiche WebSocket-Verbindung hergestellt wurde. Der Client in der Funktion Rückruf Ein WebSocket-Verbindungsobjekt repräsentiert eine Verbindung zu einem Client. Es wird verwendet, um Nachrichten zu senden und zu empfangen sowie auf Ereignisse wie Client-Nachrichten zu reagieren.

Die Funktion `distributeClientMessages` wird hier verwendet, um eingehende Nachrichten an alle verbundenen Clients zu senden. Sie nimmt ein Nachrichtenargument entgegen und durchläuft die mit dem Server verbundenen Clients. Anschließend prüft sie den Verbindungsstatus jedes Clients (`readyState === WebSocket.OPEN`). Dadurch wird sichergestellt, dass der Server nur Nachrichten an Clients mit offener Verbindung sendet. Ist die Client-Verbindung offen, sendet der Server die Nachricht mit der Methode `client.send(message)` an diesen Client.

Schritt 2 – Clientseitige Implementierung

Um die Clientseite auszuführen, die Datei index.html Wir verändern uns ein wenig.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<p>Pub/Sub Pattern with Chat Messaging</p>
<div id="messageContainer"></div>
<form id="messageForm">
<form id="messageForm">
<input
type="text"
id="messageText"
placeholder="Send a message"
style="
padding: 10px;
margin: 5px;
border-radius: 5px;
border: 1px solid #ccc;
outline: none;
"
onfocus="this.style.borderColor='#007bff';"
onblur="this.style.borderColor='#ccc';"
/>
<input
type="button"
value="Send Message"
style="
padding: 10px;
margin: 5px;
border-radius: 5px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
"
onmouseover="this.style.backgroundColor='#0056b3';"
onmouseout="this.style.backgroundColor='#007bff';"
/>
</form>
</form>
<script>
const url = window.location.host;
const socket = new WebSocket(`ws://${url}`);
</script>
</body>
</html>

In diesem Code-Ausschnitt haben wir ein Formularelement mit einem Eingabefeld und einem Button zum Senden einer Nachricht hinzugefügt. WebSocket-Verbindungen werden von Clients initiiert. Um mit einem zuvor eingerichteten WebSocket-Server zu kommunizieren, erstellen wir eine Instanz des WebSocket-Objekts mit der URL `ws://url`, die den gewünschten Server angibt. Die Variable `URL` enthält nach der Anmeldung die URL, die zu Port 3459 führt, wo unser Server lauscht.

// app.js
console.log("url", url); // localhost:3459
console.log("socket", socket); // { url: "ws://localhost:3459/", readyState: 0, bufferedAmount: 0, onopen: null, onerror: null, onclose: null, extensions: "", protocol: "", onmessage: null, binaryType: "blob" }

Wenn Sie also in Ihrem Browser eine Anfrage an den Server senden, sollten Sie Folgendes sehen:


Lasst uns unser Skript so erweitern, dass wir Nachrichten vom Client an den Server senden und Nachrichten vom Server empfangen können.

// index.html
<script>
const url = window.location.host;
const socket = new WebSocket(`ws://${url}`);
const messageContainer = document.getElementById("messageContainer");
socket.onmessage = function (eventMessage) {
eventMessage.data.text().then((text) => {
const messageContent = document.createElement("p");
messageContent.innerHTML = text;
document
.getElementById("messageContainer")
.appendChild(messageContent);
});
};
const form = document.getElementById("messageForm");
form.addEventListener("submit", (event) => {
event.preventDefault();
const message = document.getElementById("messageText").value;
socket.send(message);
document.getElementById("messageText").value = "";
});
</script>

Wie bereits erwähnt, rufen wir die URL ab, die vom Client (Browser) eine Anfrage an unseren Server sendet, und erstellen eine neue WebSocket-Objektinstanz mit dieser URL. Anschließend klicken wir auf die Schaltfläche. Nachricht senden Wir erstellen ein Ereignis für das Formularelement. Der vom Benutzer in der Benutzeroberfläche eingegebene Text wird aus dem Eingabeelement extrahiert und die Sendemethode der Socket-Instanz wird aufgerufen, um die Nachricht an den Server zu senden.

Ereignis onmessage Diese Methode wird vom Socket-Objekt aufgerufen und ausgelöst, sobald eine Nachricht vom Server empfangen wird. Sie dient dazu, die Benutzeroberfläche der empfangenen Nachricht zu aktualisieren. Parameter Ereignisnachricht In Funktion Rückruf Es enthält die vom Server gesendeten Daten (Nachricht), die jedoch als Blob zurückgegeben werden. Anschließend wird die Methode `text()` auf die Blob-Daten angewendet, welche ein Promise zurückgibt. Dieses wird mithilfe von `then()` aufgelöst, um den eigentlichen Text vom Server zu erhalten.

Testen wir, was wir haben. Starten Sie den Server mit folgendem Befehl:

node app.js

Öffnen Sie anschließend zwei verschiedene Browser-Tabs. http://localhost:3459/ Öffnen Sie die Tabs und versuchen Sie, Nachrichten zwischen ihnen zu senden, um dies zu testen:


Schritt 3 – Skalieren Sie die Anwendung

Angenommen, unsere Anwendung wächst und wir versuchen, sie durch mehrere Instanzen unseres Chat-Servers zu skalieren. Wir möchten erreichen, dass zwei verschiedene Benutzer, die mit zwei verschiedenen Servern verbunden sind, erfolgreich Textnachrichten austauschen können. Aktuell haben wir nur einen Server, und wenn wir beispielsweise einen weiteren Server anfordern, … http://localhost:3460/Servermeldungen auf Port 3459 Das wird nicht der Fall sein. Das heißt, nur Benutzer, die mit … verbunden sind. 3460 Sie können mit sich selbst chatten. Die aktuelle Implementierung funktioniert so, dass eine Chatnachricht, die auf unserer laufenden Serverinstanz gesendet wird, nur lokal an Clients verteilt wird, die mit diesem Server verbunden sind, wie hier gezeigt wird: http://localhost:3459/ Wir öffnen es in zwei verschiedenen Browsern. Schauen wir uns nun an, wie wir zwei verschiedene Server integrieren können, damit sie miteinander kommunizieren können.

Schritt 4 – Redis als Message Broker

Redis ist ein schneller und flexibler In-Memory-Datenspeicher. Er wird häufig als Datenbank oder Cache-Server zur Datenspeicherung eingesetzt. Darüber hinaus kann er zur Implementierung eines zentralisierten Pub/Sub-Nachrichtenaustauschs verwendet werden. Die Geschwindigkeit und Flexibilität von Redis haben ihn zu einer sehr beliebten Wahl für den Datenaustausch in verteilten Systemen gemacht.

Ziel ist es, unsere Chat-Server mithilfe von Redis als Message Broker zu integrieren. Jede Serverinstanz sendet alle vom Client (Browser) empfangenen Nachrichten gleichzeitig an den Message Broker. Der Message Broker abonniert alle von den Serverinstanzen gesendeten Nachrichten.

Lasst uns einreichen app.js Lasst uns uns verändern:

//app.js
const http = require("http");
const fs = require("fs");
const path = require("path");
const WebSocket = require("ws");
const Redis = require("ioredis");
const redisPublisher = new Redis();
const redisSubscriber = new Redis();
const server = http.createServer((req, res) => {
const htmlFilePath = path.join(__dirname, "index.html");
fs.readFile(htmlFilePath, (err, data) => {
if (err) {
res.writeHead(500);
res.end("Error occured while reading file");
}
res.writeHead(200, { "Content-Type": "text/html" });
res.end(data);
});
});
const webSocketServer = new WebSocket.Server({ server });
webSocketServer.on("connection", (client) => {
console.log("succesfully connected to the client");
client.on("message", (streamMessage) => {
redisPublisher.publish("chat_messages", streamMessage);
});
});
redisSubscriber.subscribe("chat_messages");
console.log("sub", redisSubscriber.subscribe("messages"));
redisSubscriber.on("message", (channel, message) => {
console.log("redis", channel, message);
for (const client of webSocketServer.clients) {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
}
});
const PORT = process.argv[2] || 3459;
server.listen(PORT, () => {
console.log(`Server up and running on port ${PORT}`);
});

Hier nutzen wir die Veröffentlichungs-/Freigabefunktionen. Redis Wir verwenden zwei verschiedene Verbindungsinstanzen: eine zum Veröffentlichen von Nachrichten und eine zum Abonnieren eines Kanals. Wenn eine Nachricht vom Client gesendet wird, wird sie mithilfe der `publisher`-Methode der `redisPublisher`-Instanz in einem Redis-Kanal namens `redisPublisher` veröffentlicht. ""Chat-Nachrichten"" Wir veröffentlichen Nachrichten. Die `subscribe`-Methode der `redisSubscribe`-Instanz wird aufgerufen, um denselben Kanal wie `chat_message` zu abonnieren. Sobald eine Nachricht in diesem Kanal veröffentlicht wird, wird der `redisSubscriber.on`-Event-Listener ausgelöst. Dieser Event-Listener wird auf allen aktuell verbundenen WebSocket-Clients wiederholt und sendet die empfangene Nachricht an jeden Client. Dadurch wird sichergestellt, dass alle mit den jeweiligen Serverinstanzen verbundenen Benutzer die Nachricht in Echtzeit erhalten, sobald ein Benutzer sie sendet.

Wenn Sie beispielsweise zwei verschiedene Server einrichten:

node app.js 3459
node app.js 3460

Wenn der Chat-Text auf einer Instanz gesendet wird, können wir die Nachrichten nun an alle verbundenen Server verteilen, anstatt nur an einen bestimmten Server. Dies erreichen Sie durch Ausführen von http://localhost:3459/ Und http://localhost:3460/ Testen Sie es, senden Sie dann Chats zwischen ihnen und sehen Sie, wie die Nachrichten in Echtzeit über die beiden Server übertragen werden.

Sie können die in einem Kanal veröffentlichten Nachrichten anzeigen von redis-cli Folgen Sie dem Kanal und abonnieren Sie ihn, um regelmäßig Nachrichten zu erhalten:

Befehl redis-cli Ausführen. Dann eingeben MONITOR Gehe zurück zu deinem Browser und beginne mit dem Chatten. In deinem Terminal solltest du etwa Folgendes sehen, vorausgesetzt, du sendest eine Wow-Chatnachricht:


Um geteilte, veröffentlichte Nachrichten anzuzeigen, verwenden Sie denselben Befehl. redis-cli Lauf und Kanalnamen abonnieren Geben Sie den Kanalnamen „Über uns“ ein. Chatnachrichten Wenn Sie eine SMS senden, sollte in Ihrem Terminal etwa Folgendes angezeigt werden: Hervorragend vom Browser aus:


Wir können nun mehrere Instanzen unseres Servers auf verschiedenen Ports oder sogar auf verschiedenen Rechnern betreiben, und solange diese denselben Redis-Kanal abonnieren, können sie Nachrichten an alle verbundenen Clients senden und empfangen, sodass die Benutzer nahtlos über verschiedene Instanzen hinweg chatten können.

Erinnern Sie sich an die Einleitung, in der wir die Implementierung des Pub/Sub-Musters mithilfe eines Message Brokers besprochen haben? Dieses Beispiel fasst es perfekt zusammen.


In der obigen Abbildung sind zwei verschiedene Clients (Browser) mit den Chat-Servern verbunden. Die Verbindung zwischen den Chat-Servern erfolgt nicht direkt, sondern über eine Redis-Instanz. Das bedeutet, dass sie zwar Client-Verbindungen unabhängig voneinander verwalten, aber Informationen (Chat-Nachrichten) über ein gemeinsames Medium (Redis) austauschen. Jeder der oben genannten Chat-Server ist mit Redis verbunden. Diese Verbindung dient dazu, Nachrichten in Redis zu veröffentlichen und Redis-Kanäle zu abonnieren, um Nachrichten zu empfangen. Wenn ein Benutzer eine Nachricht sendet, veröffentlicht der Chat-Server diese im entsprechenden Kanal in Redis.

Wenn Redis eine veröffentlichte Nachricht empfängt, sendet es diese an alle teilnehmenden Chat-Server. Jeder Chat-Server leitet die Nachricht dann an alle verbundenen Clients weiter, sodass jeder Benutzer die Nachrichten aller anderen Benutzer erhält, unabhängig davon, mit welchem Server er verbunden ist.

Diese Architektur ermöglicht es uns, unsere Chat-Anwendung horizontal zu skalieren, indem wir bei Bedarf weitere Serverinstanzen hinzufügen. Dank der Funktionen des Redis Publish/Subscribe-Systems, das eine gleichmäßige Nachrichtenverteilung über alle Instanzen gewährleistet, kann jede Instanz ihre eigenen verbundenen Clients verwalten. Dieses Setup ist effizient für die Verarbeitung einer großen Anzahl gleichzeitiger Benutzer und sichert die Verfügbarkeit Ihrer Anwendung.

Ergebnis

In diesem Tutorial haben wir das Publish/Subscribe-Muster anhand einer einfachen Chat-Anwendung mit Redis als Message Broker kennengelernt. Im nächsten Schritt lernen wir, wie man ein Peer-to-Peer-Messaging-System implementiert, insbesondere in Fällen, in denen ein Message Broker nicht die optimale Lösung darstellt, beispielsweise in komplexen verteilten Systemen, in denen ein Single Point of Failure (Broker) nicht akzeptabel ist.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Das könnte Ihnen auch gefallen