Wenn Sie PaaS als Hosting für Ihre Anwendung ausgewählt haben, hatten oder haben Sie wahrscheinlich dieses Problem: Ihre App wird in kleinen "Containern" (bekannt als Dynos in Heroku oder Getriebe in OpenShift) und Sie möchten es skalieren.
Dazu erhöhen Sie die Anzahl der Container - und jede Instanz Ihrer App wird praktisch auf einer anderen virtuellen Maschine ausgeführt. Dies ist aus mehreren Gründen sinnvoll, bedeutet aber auch, dass die Instanzen keinen Speicher gemeinsam nutzen.
In diesem Tutorial zeige ich Ihnen, wie Sie diese kleine Unannehmlichkeit überwinden können.
Wenn Sie sich für PaaS-Hosting entschieden haben, gehe ich davon aus, dass Sie Skalierung im Auge hatten. Vielleicht hat Ihre Website bereits den Slashdot-Effekt miterlebt oder möchten Sie sich darauf vorbereiten. In jedem Fall ist es ziemlich einfach, die Instanzen miteinander zu kommunizieren.
Denken Sie daran, dass ich in diesem Artikel davon ausgehen muss, dass Sie bereits eine Node.js-App geschrieben und ausgeführt haben.
Zuerst müssen Sie Ihre Redis-Datenbank vorbereiten. Ich benutze gerne Redis To Go, weil das Setup sehr schnell ist und wenn Sie Heroku verwenden, gibt es ein Add-On (obwohl Ihrem Konto eine Kreditkarte zugewiesen sein muss). Es gibt auch Redis Cloud, die mehr Speicher und Backups enthält.
Von dort ist das Heroku-Setup ziemlich einfach: Wählen Sie das Add-On auf der Heroku-Add-On-Seite aus und wählen Sie Redis Cloud oder Redis To Go aus, oder verwenden Sie einen der folgenden Befehle (beachten Sie, dass der erste Befehl für Redis To Go ist und die zweite ist für Redis Cloud):
$ heroku addons: add redistogo $ heroku addons: add rediscloud
An dieser Stelle müssen wir dem Knoten das erforderliche Knotenmodul hinzufügen package.json
Datei. Wir werden das empfohlene node_redis-Modul verwenden. Fügen Sie diese Zeile Ihrem hinzu package.json
Datei im Abschnitt Abhängigkeiten:
"node_redis": "0.11.x"
Wenn Sie möchten, können Sie auch hinzufügen Hiredis
, eine in C geschriebene Hochleistungsbibliothek, die node_redis
wird verwenden, wenn es verfügbar ist:
"Hiredis": "0,1.x"
Je nachdem, wie Sie Ihre Redis-Datenbank erstellt haben und welchen PaaS-Provider Sie verwenden, sieht der Verbindungsaufbau etwas anders aus. Du brauchst Wirt
, Hafen
, Nutzername
, und Passwort
für deine Verbindung.
Heroku speichert alles in den Konfigurationsvariablen als URLs. Sie müssen die Informationen, die Sie benötigen, mit Hilfe von Nodes extrahieren url
Modul (config var für Redis To Go ist process.env.REDISTOGO_URL
und für Redis Cloud process.env.REDISCLOUD_URL
). Dieser Code steht oben in Ihrer Hauptanwendungsdatei:
var redis = erfordern ('redis'); var url = erfordern ('url'); var redisURL = url.parse (YOUR_CONFIG_VAR_HERE); var client = redis.createClient (redisURL.host, redisURL.port); client.auth (redisURL.auth.split (':') [1]);
Wenn Sie die Datenbank von Hand erstellt haben oder einen anderen Anbieter als Heroku verwenden, sollten Sie die Verbindungsoptionen und Anmeldeinformationen bereits haben. Verwenden Sie sie also einfach:
var redis = erfordern ('redis'); var client = redis.createClient (YOUR_HOST, YOUR_PORT); client.auth (YOUR_PASSWORD);
Danach können wir mit der Kommunikation zwischen Instanzen beginnen.
Das einfachste Beispiel sendet nur Informationen an andere Instanzen, die Sie gerade gestartet haben. Sie können diese Informationen beispielsweise im Administrationsbereich anzeigen.
Bevor wir etwas tun, erstellen Sie eine weitere Verbindung mit dem Namen client2
. Ich werde später erklären, warum wir es brauchen.
Beginnen wir mit dem Senden der von uns gestarteten Nachricht. Es ist fertig mit der veröffentlichen()
Methode des Kunden. Es sind zwei Argumente erforderlich: der Kanal, an den die Nachricht gesendet werden soll, und der Nachrichtentext:
client.publish ('instanzen', 'start');
Mehr brauchen Sie nicht, um die Nachricht zu senden. Wir können auf Nachrichten im Internet hören Botschaft
Eventhandler (Beachten Sie, dass wir dies bei unserem zweiten Client aufrufen):
client2.on ('message', Funktion (Kanal, Nachricht)
Dem Callback werden die gleichen Argumente übergeben, die wir an das übergeben veröffentlichen()
Methode. Lassen Sie uns nun diese Informationen in der Konsole anzeigen:
if ((channel == 'instance') und (message == 'start')) console.log ('Neue Instanz gestartet!'); );
Als letztes müssen Sie tatsächlich den Kanal abonnieren, den wir verwenden werden:
client2.subscribe ('Instanzen');
Wir haben zwei Clients dafür verwendet, weil Sie anrufen abonnieren()
auf dem Client wird seine Verbindung auf die umgestellt Teilnehmer Modus. Ab diesem Zeitpunkt können Sie nur noch die Methoden auf dem Redis-Server aufrufen ABONNIEREN
und ABMELDEN
. Also wenn wir im sind Teilnehmer Modus können wir veröffentlichen()
Mitteilungen.
Wenn Sie möchten, können Sie auch eine Nachricht senden, wenn die Instanz heruntergefahren wird SIGTERM
Ereignis und senden Sie die Nachricht an den gleichen Kanal:
process.on ('SIGTERM', function () client.publish ('instance', 'stop'); process.exit (););
Um diesen Fall in der Botschaft
Handler fügen Sie dies hinzu sonst wenn
da drin:
sonst if ((channel == 'instance') und (message == 'stop')) console.log ('Instanz gestoppt!');
So sieht es danach aus:
client2.on ('message', Funktion (Kanal, Nachricht) if ((channel == 'instance') und (message == 'start')) console.log ('Neue Instanz gestartet!'); else if ( (channel == 'instance') und (message == 'stop')) console.log ('Instanz gestoppt!'););
Wenn Sie unter Windows testen, wird das nicht unterstützt SIGTERM
Signal.
Um es lokal zu testen, starten Sie Ihre App ein paar Mal und sehen Sie, was in der Konsole passiert. Wenn Sie die Beendigungsnachricht testen möchten, geben Sie das nicht aus Strg + C
Befehl im Terminal-stattdessen verwenden Sie die töten
Befehl. Beachten Sie, dass dies unter Windows nicht unterstützt wird. Sie können es also nicht überprüfen.
Verwenden Sie zuerst die ps
Befehl, um zu überprüfen, an welche ID Ihr Prozess es übergeben hat grep
zu erleichtern:
$ ps -aux | grep your_apps_name
Die zweite Spalte der Ausgabe ist die ID, nach der Sie suchen. Denken Sie daran, dass es auch eine Zeile für den Befehl gibt, den Sie gerade ausgeführt haben. Nun führe das aus töten
Befehl mit fünfzehn
für das Signal ist es SIGTERM
:
$ kill -15 PID
PID
ist Ihre Prozess-ID.
Nun, da Sie wissen, wie das Redis Pub / Sub-Protokoll verwendet wird, können Sie über das zuvor beschriebene einfache Beispiel hinausgehen. Hier sind einige Anwendungsfälle, die hilfreich sein können.
Dies ist äußerst hilfreich, wenn Sie Express.js als Framework verwenden. Wenn Ihre Anwendung Benutzeranmeldungen unterstützt oder praktisch alle Sitzungen, die Sitzungen verwenden, sollten Sie sicherstellen, dass die Benutzersitzungen beibehalten werden. Unabhängig davon, ob die Instanz neu gestartet wird, wechselt der Benutzer an einen Ort, der von einem anderen Benutzer oder dem Benutzer verwaltet wird wird auf eine andere Instanz umgeschaltet, da die ursprüngliche Instanz ausgefallen ist.
Ein paar Dinge zu beachten:
Wir benötigen das Connect-Redis-Modul. Die Version hängt von der verwendeten Express-Version ab. Dieser ist für Express 3.x:
"connect-redis": "1.4.7"
Und das für Express 4.x:
"connect-redis": "2.x"
Erstellen Sie nun eine weitere Redis-Verbindung mit dem Namen client_sessions
. Die Verwendung des Moduls hängt wiederum von der Express-Version ab. Für 3.x erstellen Sie die RedisStore
so was:
var RedisStore = required ('connect-redis') (express)
Und in 4.x muss man das bestehen Express-Sitzung
als Parameter:
var session = required ('express-session'); var RedisStore = required ('connect-redis') (Sitzung);
Danach ist das Setup in beiden Versionen gleich:
app.use (session (store: new RedisStore (client: client_sessions), secret: 'your secret string'));
Wie Sie sehen, übergeben wir unseren Redis-Kunden als Klient
Eigenschaft des Objekts, an das übergeben wurde RedisStore
's Konstruktor, und dann übergeben wir den Laden an die Session
Konstrukteur.
Wenn Sie jetzt Ihre App starten, sich anmelden oder eine Sitzung initiieren und die Instanz neu starten, bleibt Ihre Sitzung erhalten. Dasselbe passiert, wenn die Instanz für den Benutzer gewechselt wird.
Nehmen wir an, Sie haben eine vollständig getrennte Instanz (Arbeiter-Dyno zu Heroku), um ressourcenfressende Arbeiten wie komplizierte Berechnungen durchzuführen, Daten in der Datenbank zu verarbeiten oder eine Menge Daten mit einem externen Dienst auszutauschen. Sie möchten, dass die "normalen" Instanzen (und damit die Benutzer) das Ergebnis dieser Arbeit erfahren, wenn sie fertig sind.
Abhängig davon, ob die Webinstanzen Daten an den Worker senden sollen, benötigen Sie eine oder zwei Verbindungen (nennen wir sie.) client_sub
und client_pub
auf dem Arbeiter auch). Sie können auch jede Verbindung wiederverwenden, die nichts abonniert (wie die, die Sie für Express-Sitzungen verwenden) anstelle der client_pub
.
Wenn der Benutzer die Aktion ausführen möchte, veröffentlichen Sie die Nachricht in dem Kanal, der nur für diesen Benutzer und für diesen bestimmten Job reserviert ist:
// Dies geht in Ihren Request-Handler ein client_pub.publish ('JOB: USERID: JOBNAME: START', JSON.stringify (THEDATAYOUWANTTOSEND)); client_sub.subscribe ('JOB: USERID: JOBNAME: PROGRESS');
Natürlich musst du ersetzen BENUTZERIDENTIFIKATION
und BERUFSBEZEICHNUNG
mit entsprechenden Werten. Sie sollten auch die haben Botschaft
Handler bereit für die client_sub
Verbindung:
client_on ('message', Funktion (Kanal, Nachricht) var USERID = channel.split (':') [1]; if (message == 'DONE') client_sub.unsubscribe (channel); sockets [USERID] .emit (Kanal, Nachricht););
Dies extrahiert das BENUTZERIDENTIFIKATION
aus dem Kanalnamen (stellen Sie sicher, dass Sie keine Kanäle abonnieren, die nicht mit Benutzeraufträgen für diese Verbindung zusammenhängen) und senden Sie die Nachricht an den entsprechenden Client. Je nachdem, welche WebSocket-Bibliothek Sie verwenden, gibt es eine Möglichkeit, auf einen Socket über seine ID zuzugreifen.
Sie fragen sich vielleicht, wie die Worker-Instanz all diese Kanäle abonnieren kann. Natürlich möchten Sie nicht nur ein paar Loops auf allen möglichen Wegen machen BENUTZERIDENTIFIKATION
s und BERUFSBEZEICHNUNG
s. Das psubscribe ()
Die Methode akzeptiert ein Muster als Argument, sodass alle abonniert werden können JOB:*
Kanäle:
// Dieser Code geht an die Worker-Instanz // und Sie nennen ihn ONCE client_sub.psubscribe ('JOB: *').
Bei der Verwendung von Pub / Sub können einige Probleme auftreten:
Botschaft
Handler vor dem Aufruf abonnieren()
, und dass du anrufst abonnieren()
einmal, bevor Sie anrufen veröffentlichen()
auf dem anderen.