Als u PaaS als hosting voor uw toepassing hebt gekozen, heeft u waarschijnlijk dit probleem gehad of zal dit zich voordoen: uw app wordt geïmplementeerd in kleine "containers" (bekend als dynos in Heroku, of versnellingen in OpenShift) en u wilt het schalen.
Om dit te doen, verhoogt u het aantal containers - en elk exemplaar van uw app wordt vrijwel uitgevoerd op een andere virtuele machine. Dit is goed om een aantal redenen, maar het betekent ook dat de instanties geen geheugen delen.
In deze tutorial zal ik je laten zien hoe je dit kleine ongemak kunt overwinnen.
Toen je PaaS-hosting koos, neem ik aan dat je rekening had gehouden met schalen. Misschien is uw site al getuige geweest van het Slashdot-effect of wilt u zich erop voorbereiden. Hoe dan ook, het maken van de instanties om met elkaar te communiceren is vrij eenvoudig.
Houd er rekening mee dat ik in het artikel ga ervan uit dat je al een Node.js-app hebt geschreven en gebruikt.
Eerst moet je je Redis-database voorbereiden. Ik gebruik Redis To Go graag, omdat de instelling erg snel is en als je Heroku gebruikt, is er een add-on (hoewel je account aan een creditcard moet zijn toegewezen). Er is ook Redis Cloud, die meer opslag en back-ups bevat.
Vanaf daar is de Heroku-setup vrij eenvoudig: selecteer de add-on op de Heroku-add-onspagina en selecteer Redis Cloud of Redis To Go of gebruik een van de volgende opdrachten (merk op dat de eerste is voor Redis To Go , en de tweede is voor Redis Cloud):
$ heroku addons: add redistogo $ heroku addons: voeg rediscloud toe
Op dit punt moeten we de vereiste knooppuntmodule toevoegen aan de package.json
het dossier. We gebruiken de aanbevolen module node_redis. Voeg deze regel toe aan uw package.json
bestand, in de sectie afhankelijkheden:
"node_redis": "0.11.x"
Als je wilt, kun je ook opnemen hiredis
, een high-performance bibliotheek geschreven in C, die node_redis
zal gebruiken als het beschikbaar is:
"hiredis": "0.1.x"
Afhankelijk van hoe je je Redis-database hebt gemaakt en welke PaaS-provider je gebruikt, zal de verbindingsinstellingen er iets anders uitzien. Jij hebt nodig gastheer
, haven
, gebruikersnaam
, en wachtwoord
voor uw verbinding.
Heroku slaat alles in de config-variabelen op als URL's. Je moet de informatie die je nodig hebt extraheren met behulp van Node's url
module (config var voor Redis To Go is process.env.REDISTOGO_URL
en voor Redis Cloud process.env.REDISCLOUD_URL
). Deze code staat bovenaan uw hoofdtoepassingsbestand:
var redis = require ('redis'); var url = require ('url'); var redisURL = url.parse (YOUR_CONFIG_VAR_HERE); var client = redis.createClient (redisURL.host, redisURL.port); client.auth (redisURL.auth.split ( ':') [1]);
Als u de database handmatig hebt gemaakt of een andere aanbieder dan Heroku hebt gebruikt, moet u al over de verbindingsopties en legitimatiegegevens beschikken, dus gebruik ze gewoon:
var redis = require ('redis'); var client = redis.createClient (YOUR_HOST, YOUR_PORT); client.auth (your_password);
Daarna kunnen we gaan werken aan communicatie tussen instanties.
Het eenvoudigste voorbeeld zal alleen informatie verzenden naar andere instanties die u net bent gestart. U kunt deze informatie bijvoorbeeld weergeven in het beheerdersdashboard.
Maak voordat we iets doen een nieuwe verbinding met de naam client2
. Ik zal uitleggen waarom we het later nodig hebben.
Laten we beginnen met het verzenden van het bericht dat we zijn gestart. Het is gedaan met behulp van de publiceren()
methode van de klant. Er zijn twee argumenten voor nodig: het kanaal waarnaar we het bericht willen verzenden en de tekst van het bericht:
client.publish ('instanties', 'start');
Dat is alles wat u nodig hebt om het bericht te verzenden. We kunnen luisteren naar berichten in de bericht
event handler (merk op dat we dit op onze tweede client noemen):
client2.on ('bericht', functie (kanaal, bericht)
De callback krijgt dezelfde argumenten die we doorgeven aan de publiceren()
methode. Laten we deze informatie nu in de console weergeven:
if ((channel == 'instanties') en (message == 'start')) console.log ('Nieuw exemplaar gestart!'); );
Het laatste wat je moet doen is je daadwerkelijk abonneren op het kanaal dat we gaan gebruiken:
client2.subscribe (instances);
We hebben hiervoor twee clients gebruikt, omdat je belt abonneren ()
op de client wordt de verbinding omgeschakeld naar de abonnee modus. Vanaf dat moment zijn de enige methoden die u op de Redis-server kunt gebruiken INSCHRIJVEN
en AFMELDEN
. Dus als we in de abonnee modus die we kunnen publiceren()
berichten.
Als u wilt, kunt u ook een bericht verzenden wanneer het exemplaar wordt afgesloten. U kunt naar de SIGTERM
evenement en stuur het bericht naar hetzelfde kanaal:
process.on ('SIGTERM', functie () client.publish ('instanties', 'stop'); process.exit (););
Om die zaak in de bericht
handler voeg dit toe anders als
daarin:
else if ((channel == 'instances') en (message == 'stop')) console.log ('Exemplaar gestopt!');
Dus het ziet er na afloop als volgt uit:
client2.on ('message', function (channel, message) if ((channel == 'instances') en (message == 'start')) console.log ('New instance started!'); else if ( (kanaal == 'instanties') en (bericht == 'stop')) console.log ('Instance stopped!'););
Merk op dat als u aan het testen bent op Windows, dit niet de SIGTERM
signaal.
Als u het lokaal wilt testen, start u uw app een paar keer en ziet u wat er in de console gebeurt. Als u het beëindigingsbericht wilt testen, geeft u het bericht niet uit Ctrl + C
commando in de terminal - gebruik in plaats daarvan de doden
commando. Merk op dat dit niet wordt ondersteund in Windows, dus u kunt het niet controleren.
Gebruik eerst de ps
opdracht om te controleren naar welk id uw proces heeft - leid het naar grep
om het makkelijker te maken:
$ ps -aux | grep your_apps_name
De tweede kolom van de uitvoer is de ID waarnaar u op zoek bent. Houd er rekening mee dat er ook een regel is voor het commando dat u net hebt uitgevoerd. Voer nu de doden
commando gebruiken 15
voor het signaal-het is SIGTERM
:
$ kill -15 PID
PID
is uw proces-ID.
Nu u weet hoe u het Redis Pub / Sub-protocol kunt gebruiken, kunt u verder gaan dan het eenvoudige voorbeeld dat eerder is gepresenteerd. Hier zijn een paar gebruiksgevallen die nuttig kunnen zijn.
Deze is erg handig als je Express.js als kader gebruikt. Als uw toepassing gebruikersaanmeldingen of vrijwel alles met sessies ondersteunt, moet u ervoor zorgen dat de gebruikerssessies worden behouden, ongeacht of het exemplaar opnieuw wordt opgestart, de gebruiker naar een locatie gaat die door een ander wordt behandeld, of de gebruiker is overgeschakeld naar een andere instantie omdat de oorspronkelijke is verdwenen.
Een paar dingen om te onthouden:
We hebben de module connect-redis nodig. De versie is afhankelijk van de versie van Express die u gebruikt. Deze is voor Express 3.x:
"connect-redis": "1.4.7"
En dit voor Express 4.x:
"connect-redis": "2.x"
Maak nu een andere Redis-verbinding met de naam client_sessions
. Het gebruik van de module is weer afhankelijk van de Express-versie. Voor 3.x maak je de RedisStore
zoals dit:
var RedisStore = vereisen ('connect-redis') (express)
En in 4.x moet je de express-sessie
als de parameter:
var session = require ('express-sessie'); var RedisStore = vereisen ('connect-redis') (sessie);
Daarna is de setup in beide versies hetzelfde:
app.use (sessie (store: new RedisStore (client: client_sessions), geheim: 'your secret string'));
Zoals je kunt zien, passeren we onze Redis-client als de cliënt
eigenschap van het object doorgegeven aan RedisStore
's constructor, en dan passeren we de winkel naar de sessie
bouwer.
Als u nu uw app start, inlogt of een sessie start en de instance herstart, blijft uw sessie behouden. Hetzelfde gebeurt wanneer de instantie wordt omgeschakeld naar de gebruiker.
Laten we zeggen dat je een volledig gescheiden instantie hebt (werknemer dyno op Heroku) voor het doen van meer middelen-etend werk zoals ingewikkelde berekeningen, het verwerken van gegevens in de database of het uitwisselen van veel gegevens met een externe dienst. U wilt dat de "normale" instanties (en dus de gebruikers) het resultaat van dit werk weten wanneer het klaar is.
Afhankelijk van of u wilt dat de webexemplaren gegevens naar de werknemer verzenden, hebt u een of twee verbindingen nodig (laten we ze een naam geven) client_sub
en client_pub
ook op de werknemer). U kunt ook elke verbinding opnieuw gebruiken die niet op iets is geabonneerd (zoals degene die u gebruikt voor Express-sessies) in plaats van de client_pub
.
Wanneer de gebruiker de actie wil uitvoeren, publiceert u het bericht op het kanaal dat alleen voor deze gebruiker en voor deze specifieke taak is gereserveerd:
// dit gaat in uw request handler client_pub.publish ('JOB: USERID: JOBNAME: START', JSON.stringify (THEDATAYOUWANTTOSEND)); client_sub.subscribe ( 'JOB: USERID: TAAKNAAM: PROGRESS');
Natuurlijk moet je het vervangen GEBRUIKERSNAAM
en TAAKNAAM
met geschikte waarden. Je zou ook de bericht
manager voorbereid op de client_sub
verbinding:
client_sub.on ('bericht', functie (kanaal, bericht) var USERID = channel.split (':') [1]; if (message == 'DONE') client_sub.unsubscribe (channel); sockets [USERID] .emit (kanaal, bericht););
Dit haalt de GEBRUIKERSNAAM
van de kanaalnaam (dus zorg ervoor dat u zich niet abonneert op kanalen die geen verband houden met gebruikerstaken op deze verbinding) en verzend het bericht naar de juiste client. Afhankelijk van welke WebSocket-bibliotheek u gebruikt, is er een manier om toegang te krijgen tot een socket via zijn ID.
U kunt zich afvragen hoe de worker-instantie zich op al deze kanalen kan abonneren. Natuurlijk wil je niet alleen een paar loops doen op alle mogelijke manieren GEBRUIKERSNAAM
s en TAAKNAAM
s. De psubscribe ()
methode accepteert een patroon als het argument, zodat het zich voor iedereen kan abonneren JOB: *
kanalen:
// deze code gaat naar de worker instance // en je noemt het ONCE client_sub.psubscribe ('JOB: *')
Er zijn een paar problemen die u kunt tegenkomen bij het gebruik van Pub / Sub:
bericht
handler voor het bellen abonneren ()
, en dat je belt abonneren ()
in één instantie voordat u belt publiceren()
op de andere.