Hvis du valgte PaaS som vert for din søknad, har du sannsynligvis eller vil dette problemet: Appen din er distribuert til små "containere" (kjent som dynos i Heroku, eller tannhjul i OpenShift) og du vil skalere den.
For å gjøre det øker du antall containere, og hver forekomst av appen din går ganske mye i en annen virtuell maskin. Dette er bra av flere grunner, men det betyr også at forekomstene ikke deler minnet.
I denne opplæringen vil jeg vise deg hvordan du kan overvinne denne lille ulempen.
Når du valgte PaaS hosting, antar jeg at du hadde skalering i tankene. Kanskje nettstedet ditt allerede har sett Slashdot-effekten, eller du vil forberede deg på det. Uansett, det er ganske enkelt å få instansene til å kommunisere med hverandre.
Husk at i artikkelen vil jeg anta at du allerede har en Node.js-app skrevet og kjørt.
Først må du forberede Redis-databasen. Jeg liker å bruke Redis To Go, fordi oppsettet er veldig raskt, og hvis du bruker Heroku, er det et tillegg (selv om kontoen din må ha et kredittkort tildelt det). Det er også Redis Cloud, som inkluderer mer lagring og sikkerhetskopiering.
Derfra er Heroku-oppsettet ganske enkelt: Velg tillegget på siden Heroku Add-ons, og velg Redis Cloud eller Redis To Go, eller bruk en av følgende kommandoer (merk at den første er for Redis To Go , og den andre er for Redis Cloud):
$ heroku addons: legg til redistogo $ heroku addons: legg til rediscloud
På dette tidspunktet må vi legge til nødvendig nodemodul til package.json
fil. Vi vil bruke den anbefalte node_redis-modulen. Legg denne linjen til din package.json
fil, i avhengighetsdelen:
"node_redis": "0.11.x"
Hvis du vil, kan du også inkludere hiredis
, et høyytelsesbibliotek skrevet i C, som node_redis
vil bruke hvis den er tilgjengelig:
"hiredis": "0.1.x"
Avhengig av hvordan du opprettet Redis-databasen og hvilken PaaS-leverandør du bruker, vil tilkoblingsoppsettet se litt annerledes ut. Du trenger vert
, havn
, brukernavn
, og passord
for tilkoblingen din.
Heroku lagrer alt i config-variablene som nettadresser. Du må trekke ut informasjonen du trenger fra dem ved hjelp av Node url
modulen (config var for Redis To Go er process.env.REDISTOGO_URL
og for redis cloud process.env.REDISCLOUD_URL
). Denne koden går øverst på hovedprogramfilen din:
var redis = krever ('redis'); var url = krever ('url'); var redisURL = url.parse (YOUR_CONFIG_VAR_HERE); var klient = redis.createClient (redisURL.host, redisURL.port); client.auth (redisURL.auth.split ( ':') [1]);
Hvis du opprettet databasen for hånd, eller bruker en annen leverandør enn Heroku, bør du allerede ha tilkoblingsmuligheter og referanser, så bare bruk dem:
var redis = krever ('redis'); var klient = redis.createClient (YOUR_HOST, YOUR_PORT); client.auth (ditt_passord);
Deretter kan vi begynne å jobbe med kommunikasjon mellom forekomster.
Det enkleste eksempelet vil bare sende informasjon til andre tilfeller du nettopp har startet. For eksempel kan du vise denne informasjonen i administrasjonspanelet.
Før vi gjør noe, opprett en annen tilkobling som heter client2
. Jeg vil forklare hvorfor vi trenger det senere.
La oss starte med bare å sende meldingen som vi startet. Det er gjort ved hjelp av publisere()
metode for klienten. Det tar to argumenter: Kanalen vi vil sende meldingen til, og meldingsteksten:
client.publish ('forekomster', 'start');
Det er alt du trenger for å sende meldingen. Vi kan lytte etter meldinger i budskap
hendelsehandler (legg merke til at vi kaller dette på vår andre klient):
client2.on ('message', funksjon (kanal, melding)
Tilbakeringingen er bestått de samme argumentene som vi sender til publisere()
metode. La oss nå vise denne informasjonen i konsollen:
hvis ((kanal == 'forekomster') og (melding == 'start')) console.log ('Ny forekomst startet!'); );
Den siste tingen å gjøre er å faktisk abonnere på kanalen vi skal bruke:
client2.subscribe ( 'tilfeller');
Vi brukte to klienter for dette fordi når du ringer abonnere()
På klienten blir tilkoblingen tilkoblet til abonnenten modus. Fra det tidspunktet er de eneste metodene du kan ringe på Redis-serveren ABONNERE
og AVSLUTTE ABONNEMENTET
. Så hvis vi er i abonnenten modus vi kan publisere()
meldinger.
Hvis du vil, kan du også sende en melding når forekomsten slås av - du kan lytte til SIGTERM
hendelse og send meldingen til samme kanal:
process.on ('SIGTERM', funksjon () client.publish ('forekomster', 'stopp'); process.exit (););
Å håndtere dette tilfellet i budskap
handler legg til dette eller hvis
der inne
ellers hvis ((kanal == 'forekomster') og (melding == 'stopp')) console.log ('Instans stoppet!');
Så ser det ut som dette etterpå:
client2.on ('message', funksjon (kanal, melding) if ((channel == 'instances') og (message == 'start')) console.log ('Ny forekomst startet!'); (kanal == 'forekomster') og (melding == 'stopp')) console.log ('Instans stoppet!'););
Merk at hvis du prøver på Windows, støtter den ikke SIGTERM
signal.
For å teste det lokalt, start programmet et par ganger og se hva som skjer i konsollen. Hvis du vil teste avslutningsmeldingen, må du ikke utstede Ctrl + C
kommandoen i terminalen-istedenfor, bruk drepe
kommando. Merk at dette ikke støttes på Windows, så du kan ikke sjekke det.
Bruk først ps
kommandoen for å sjekke hvilket id prosessen din har - rør den til grep
for å gjøre det enklere:
$ ps -aux | grep your_apps_name
Den andre kolonnen i utgangen er IDen du ser etter. Husk at det også vil være en linje for kommandoen du bare kjørte. Kjør nå drepe
kommandoen bruker 15
for signalet - det er det SIGTERM
:
$ drepe -15 PID
PID
er din prosess-ID.
Nå som du vet hvordan du bruker Redis Pub / Sub-protokollen, kan du gå utover det enkle eksempelet som ble presentert tidligere. Her er noen brukssaker som kan være nyttige.
Denne er ekstremt nyttig hvis du bruker Express.js som rammeverk. Hvis din søknad støtter brukerinnlogginger, eller stort sett alt som bruker økter, vil du sørge for at brukerens økter blir bevart, uansett om forekomsten starter på nytt, flyttes brukeren til et sted som håndteres av en annen, eller brukeren Byttes til en annen forekomst fordi den opprinnelige gikk ned.
Noen få ting å huske:
Vi trenger Connect-Redis-modulen. Versjonen avhenger av hvilken versjon av Express du bruker. Denne er for Express 3.x:
"connect-redis": "1.4.7"
Og dette for Express 4.x:
"connect-redis": "2.x"
Opprett nå en annen Redis-forbindelse som heter client_sessions
. Bruken av modulen avhenger igjen av Express-versjonen. For 3.x lager du RedisStore
som dette:
var RedisStore = krever ('connect-redis') (uttrykk)
Og i 4.x må du passere ekspress-session
som parameter:
var session = krever ('express-session'); var RedisStore = krever ('connect-redis') (økt);
Etter det er oppsettet det samme i begge versjoner:
app.use (økt (butikk: ny RedisStore (klient: client_sessions), hemmelig: 'din hemmelige streng'));
Som du ser, passerer vi vår Redis-klient som klient
eiendommen til objektet passert til RedisStore
Konstruktør, og så passerer vi butikken til økt
konstruktør.
Nå, hvis du starter appen din, logger deg på eller starter en økt og starter på nytt, vil økten din bli bevart. Det samme skjer når forekomsten er byttet til brukeren.
La oss si at du har en helt adskilt forekomst (arbeider dyno på Heroku) for å gjøre mer ressurs-spise arbeid som kompliserte beregninger, behandling av data i databasen, eller utveksling av mye data med en ekstern tjeneste. Du vil ha "vanlige" forekomster (og dermed brukerne) for å få vite resultatet av dette arbeidet når det er gjort.
Avhengig av om du vil at webinstansene skal sende data til arbeideren, trenger du en eller to tilkoblinger (la oss kalle dem client_sub
og client_pub
på arbeideren også). Du kan også gjenbruke en forbindelse som ikke abonnerer på noe (som den du bruker til Express-økter) i stedet for client_pub
.
Nå når brukeren ønsker å utføre handlingen, publiserer du meldingen på kanalen som er reservert bare for denne brukeren og for denne spesifikke jobben:
// dette går inn i forespørselsbehandleren client_pub.publish ('JOB: USERID: JOBNAME: START', JSON.stringify (THEDATAYOUWANTTOSEND)); client_sub.subscribe ( 'JOB: USERID: JOBBNAVN: PROGRESS');
Selvfølgelig må du erstatte BRUKER-ID
og JOBB NAVN
med passende verdier. Du bør også ha budskap
handler forberedt på client_sub
forbindelse:
klient_sub.on ('melding', funksjon (kanal, melding) var USERID = channel.split (':') [1], hvis (melding == 'DONE') client_sub.unsubscribe (kanal); stikkontakter [USERID] .emit (kanal, melding););
Dette trekker ut BRUKER-ID
fra kanalnavnet (så pass på at du ikke abonnerer på kanaler som ikke er relatert til brukerjobber på denne tilkoblingen), og sender meldingen til den aktuelle klienten. Avhengig av hvilket WebSocket-bibliotek du bruker, vil det være noen måte å få tilgang til en kontakt med sin ID.
Du lurer kanskje på hvordan arbeidstakerens forekomst kan abonnere på alle disse kanalene. Selvfølgelig vil du ikke bare gjøre noen løkker på alle mulige BRUKER-ID
s og JOBB NAVN
s. De psubscribe ()
Metoden aksepterer et mønster som argumentet, slik at det kan abonnere på alle JOBB:*
kanaler:
// denne koden går til arbeidstakers forekomst // og du kaller det EN gang client_sub.psubscribe ('JOB: *')
Det er noen problemer du kan støte på når du bruker Pub / Sub:
budskap
handler før du ringer abonnere()
, og det du ringer til abonnere()
i ett tilfelle før du ringer publisere()
på den andre.