Dacă ați ales PaaS ca găzduire pentru aplicația dvs., ați avut probabil sau veți avea această problemă: aplicația dvs. este distribuită în mici "containere" (cunoscute sub numele de dynos în Heroku, sau unelte în OpenShift) și doriți să o scalați.
Pentru a face acest lucru, creșteți numărul de containere - și fiecare instanță a aplicației dvs. se execută aproape într-o altă mașină virtuală. Acest lucru este bun din mai multe motive, dar înseamnă, de asemenea, că instanțele nu împart memoria.
În acest tutorial vă voi arăta cum să depășiți acest mic neplăcut.
Când ați ales hostingul PaaS, presupun că ați avut în minte. Poate că site-ul dvs. a fost deja martorul efectului Slashdot sau doriți să vă pregătiți pentru el. Oricum, comunicarea dintre instanțe este una simplă.
Rețineți că în articol voi presupune că aveți deja o aplicație Node.js scrisă și rulată.
În primul rând, trebuie să vă pregătiți baza de date Redis. Îmi place să folosesc Redis To Go, deoarece setarea este foarte rapidă și dacă utilizați Heroku există un add-on (deși contul dvs. trebuie să aibă un card de credit atribuit acestuia). Există, de asemenea, Redis Cloud, care include mai mult spațiu de stocare și copii de rezervă.
De aici, instalarea Heroku este destul de ușoară: selectați add-on-ul de pe pagina de Suplimente Heroku și selectați Redis Cloud sau Redis To Go sau utilizați una din următoarele comenzi (rețineți că prima este pentru Redis To Go , iar al doilea este pentru Redis Cloud):
Addons pentru heroku: adaugă redistogo $ heroku addons: adaugă rediscloud
În acest moment, trebuie să adăugăm modulul Nod necesar la package.json
fişier. Vom folosi modulul node_redis recomandat. Adăugați această linie la dvs. package.json
fișier, în secțiunea dependențe:
"node_redis": "0.11.x"
Dacă doriți, puteți include, de asemenea hiredis
, o bibliotecă de înaltă performanță scrise în C, care node_redis
va folosi dacă este disponibil:
"hiredis": "0.1.x"
În funcție de modul în care ați creat baza de date Redis și de furnizorul de servicii PaaS pe care îl utilizați, configurarea conexiunii va părea puțin diferită. Ai nevoie gazdă
, port
, nume de utilizator
, și parola
pentru conexiunea ta.
Heroku stochează totul în variabilele config ca adrese URL. Trebuie să extrageți informațiile de care aveți nevoie folosind Nodurile URL-ul
modul (config var pentru Redis To Go este process.env.REDISTOGO_URL
și pentru Redis Cloud process.env.REDISCLOUD_URL
). Acest cod merge în partea de sus a fișierului principal de aplicație:
var redis = cer ('redis'); var url = cer ('url'); var redisURL = url.parse (YOUR_CONFIG_VAR_HERE); var client = redis.createClient (redisURL.host, redisURL.port); client.auth (redisURL.auth.split ( ':') [1]);
Dacă ați creat baza de date manuală sau ați folosit un alt furnizor decât Heroku, ar trebui să aveți deja opțiunile de conectare și acreditările, deci pur și simplu să le utilizați:
var redis = cer ('redis'); var client = redis.createClient (YOUR_HOST, YOUR_PORT); client.auth (parola_ta);
După aceasta, putem începe să lucrăm la comunicarea dintre instanțe.
Cel mai simplu exemplu va trimite doar informații altor instanțe pe care tocmai ați început. De exemplu, puteți afișa aceste informații în panoul de administrare.
Înainte de a face ceva, creați o altă conexiune numită Client2
. Voi explica de ce avem nevoie mai târziu.
Să începem prin trimiterea mesajului pe care l-am început. Sa făcut folosind publica()
metodă a clientului. Este nevoie de două argumente: canalul la care vrem să trimitem mesajul și textul mesajului:
client.publish ("instanțe", "începe");
Asta e tot ce ai nevoie pentru a trimite mesajul. Putem asculta mesaje în mesaj
manipulator de evenimente (observați că îl numim pe cel de-al doilea client):
client2.on ('mesaj', funcție (canal, mesaj)
Callback-ul este trecut pe aceleași argumente pe care le trecem la publica()
metodă. Acum, să afișăm aceste informații în consola:
dacă ((canal == "instanțe") și (message == 'start')) console.log ("A început o nouă instanță!"); );
Ultimul lucru pe care trebuie să-l faceți este să vă abonați de fapt la canalul pe care îl vom folosi:
client2.subscribe ( 'cazuri');
Am folosit doi clienți pentru asta, deoarece când sunați Abonati-va()
pe client, conexiunea sa este comutată pe abonat Mod. Din acest punct, singurele metode pe care le puteți apela pe serverul Redis sunt ABONATI-VA
și UNSUBSCRIBE
. Deci, dacă suntem în abonat modul în care putem publica()
mesaje.
Dacă doriți, puteți trimite și un mesaj când instanța este oprită - puteți asculta mesajul SIGTERM
și trimiteți mesajul la același canal:
process.on ('SIGTERM', funcția () client.publish ('instanțe', 'stop'); process.exit (););
Pentru a rezolva cazul respectiv în mesaj
handler adaugă acest lucru altfel dacă
acolo:
altfel dacă ((canal == 'instanțe') și (message == 'stop')) console.log ('instanța a fost oprită!');
Asa se intampla astfel:
client2.on ('mesaj', functie (canal, mesaj) if ((canal == 'instanta') si (message == 'start') console.log (canal == 'instanțe') și (message == 'stop')) console.log ('instanța a fost oprită!'););
Rețineți că, dacă testați pe Windows, nu suportă SIGTERM
semnal.
Pentru a le testa local, porniți aplicația de câteva ori și vedeți ce se întâmplă în consola. Dacă doriți să testați mesajul de terminare, nu emiteți Ctrl + C
comanda în terminal - în schimb, utilizați ucide
comanda. Rețineți că acest lucru nu este acceptat pe Windows, deci nu îl puteți verifica.
Mai întâi, utilizați ps
comanda pentru a verifica ce ID-ul dvs. proces-a-pipe-l grep
a face mai ușor:
$ ps -aux | grep your_apps_name
A doua coloană a ieșirii este ID-ul pentru care căutați. Rețineți că va exista și o linie pentru comanda pe care tocmai ați fugit-o. Acum executați ucide
folosind comanda 15
pentru semnal - este SIGTERM
:
$ ucide -15 PID
PID
este ID-ul procesului.
Acum că știți cum să utilizați protocolul Redis Pub / Sub, puteți trece dincolo de exemplul simplu prezentat mai devreme. Iată câteva cazuri de utilizare care pot fi utile.
Acest lucru este extrem de util dacă utilizați Express.js ca cadru. Dacă aplicația dvs. acceptă datele de conectare ale utilizatorilor sau ceva ce utilizează sesiunile, veți dori să vă asigurați că sesiunile de utilizatori sunt conservate, indiferent dacă instanța repornește, utilizatorul se deplasează într-o locație care este gestionată de un alt utilizator sau utilizatorul este schimbată într-o altă instanță, deoarece cea originală a căzut.
Cateva lucruri de retinut:
Vom avea nevoie de modulul connect-redis. Versiunea depinde de versiunea Express pe care o utilizați. Aceasta este pentru Express 3.x:
"connect-redis": "1.4.7"
Și pentru Express 4.x:
"connect-redis": "2.x"
Acum creați o altă conexiune Redis numită client_sessions
. Utilizarea modulului depinde din nou de versiunea Express. Pentru 3.x creați RedisStore
asa:
var RedisStore = necesită ('connect-redis') (expres)
Și în 4.x trebuie să treci Express-sesiune
ca parametru:
var sesiune = necesită ("sesiune expresă"); var RedisStore = necesită ('connect-redis') (sesiune);
După aceea, configurația este aceeași în ambele variante:
app.use (sesiune (store: new RedisStore (client: client_sessions), secret: "șirul tău secret"));
Dupa cum vedeti, noi trecem pe clientul nostru Redis ca client
proprietatea obiectului trecut RedisStore
constructor, apoi vom trece magazinul la sesiune
constructor.
Acum, dacă începeți aplicația, conectați-vă sau inițiați o sesiune și reporniți instanța, sesiunea dvs. va fi păstrată. Același lucru se întâmplă atunci când instanța este schimbată pentru utilizator.
Să presupunem că aveți o instanță complet separată (lucrător dyno pe Heroku) pentru a face mai multe lucrări cu resurse, cum ar fi calcule complicate, prelucrarea datelor în baza de date sau schimbul de date cu un serviciu extern. Veți dori instanțele "normale" (și, prin urmare, utilizatorii) să cunoască rezultatul acestei lucrări când este terminat.
În funcție de faptul dacă doriți ca instanțele Web să trimită lucrătorului date cu date, veți avea nevoie de una sau două conexiuni (să le numim client_sub
și client_pub
și pe muncitor). De asemenea, puteți reutiliza orice conexiune care nu se abonează la nimic (ca cel pe care îl utilizați pentru sesiunile Express) în loc de client_pub
.
Acum când utilizatorul dorește să efectueze acțiunea, publicați mesajul pe canalul rezervat doar pentru acest utilizator și pentru această lucrare specifică:
// aceasta merge în solicitantul dvs. de solicitare client_pub.publish ("JOB: USERID: JOBNAME: START", JSON.stringify (THEDATAYOUWANTTOSEND)); client_sub.subscribe ( 'JOB: USERID: jobname: PROGRESS');
Desigur, va trebui să înlocuiți NUMELE DE UTILIZATOR
și NUMELE LOCULUI DE MUNCA
cu valori corespunzătoare. Ar trebui să aveți și mesaj
manipulant pregătit pentru client_sub
conexiune:
client_sub.on ('mesaj', funcție (canal, mesaj) var USERID = canal.split (':') [1] .emit (canal, mesaj););
Aceasta extrage NUMELE DE UTILIZATOR
de la numele canalului (asigurați-vă că nu vă abonați la canalele care nu sunt legate de operațiile utilizatorului în această conexiune) și trimiteți mesajul clientului corespunzător. În funcție de biblioteca WebSocket pe care o utilizați, va exista o modalitate de a accesa un soclu prin codul său de identificare.
Vă puteți întreba cum se poate subscrie instanța lucrătorilor la toate aceste canale. Desigur, nu vrei doar să faci câteva bucle pe toate posibilurile NUMELE DE UTILIZATOR
și NUMELE LOCULUI DE MUNCA
s. psubscribe ()
metoda acceptă un model ca argument, astfel încât să se poată abona la toate LOC DE MUNCA:*
canale:
// acest cod merge la instanța muncitorului // și îl apelați la client_sub.psubscribe ('JOB: *')
Există câteva probleme pe care le puteți întâlni când folosiți Pub / Sub:
mesaj
handler înainte de a apela Abonati-va()
, și pe care îl chemați Abonati-va()
pe o singură instanță înainte de a apela publica()
pe de altă parte.