A Socket.io hozzáadása a többszálú Node.js fájlhoz

Fotó: Vidar Nordli-Mathisen az Unsplash-en

A Node egyik hátránya, hogy egyszálú. Természetesen van egy út körül is - nevezetesen a fürtnek nevezett modul. A klaszter lehetővé teszi számunkra, hogy alkalmazásunkat több szálra terjesszük.

Most azonban egy új probléma jelent meg. Lásd: a több példányon futó kódunk valójában jelentős hátrányokkal rendelkezik. Egyikük nem rendelkezik globális államokkal.

Általában egy menetes esetben ez nem lenne sok aggodalom. Nekünk most mindent megváltoztat.

Lássuk miért.

Szóval mi a probléma?

Alkalmazásunk egy egyszerű online csevegés, amely négy szálon fut. Ez lehetővé teszi a felhasználó számára, hogy egyidejűleg bejelentkezzen a telefonján és a számítógépén.

Képzelje el, hogy a foglalatok pontosan úgy vannak beállítva, mintha egy szálra beállítottuk volna őket. Más szavakkal, most egy nagy globális állam van, aljzatokkal.

Amikor a felhasználó bejelentkezik a számítógépén, a webhely megnyitja a kapcsolatot egy kiszolgálónkon lévő Socket.io példánygal. A foglalat a 3. számú szál állapotában van tárolva.

Most képzelje el, hogy a felhasználó megy a konyhába, hogy megragadjon egy harapnivalót, és vigye magával a telefonját - természetesen azt akarja, hogy online maradjon a srácok barátaival.

Telefonjuk csatlakozik a 4. szálathoz, és az aljzat a szál állapotában van elmentve.

Ha üzenetet küldenek a telefonjukról, a felhasználónak nem lesz jó eredménye. Csak az # 3 szálakból származó emberek láthatják az üzenetet. Ennek oka az, hogy a 3. szálat mentett aljzatok nem varázslatosan tárolódnak az 1., 2. és 4. szálakon is.

Elég vicces, hogy még a felhasználó sem látja üzenetét a számítógépen, miután visszatér a konyhából.

Természetesen, amikor frissítik a weboldalt, elküldhetünk egy GET-kérést, és letölthetjük az utolsó 50 üzenetet, de nem mondhatjuk valójában, hogy ez a „dinamikus” módszer, ugye?

Miért történik ez?

Szerverünk több szálon történő elterjesztése valamilyen módon azzal egyenértékű, hogy több különálló kiszolgálóval rendelkezzünk. Nem tudnak egymás létezéséről, és minden bizonnyal nem osztanak memóriát. Ez azt jelenti, hogy az egyik példány nem létezik a másik objektumon.

A 3. szálatba mentett aljzatok nem feltétlenül mindegyik aljzat, amelyet a felhasználó jelenleg használ. Ha a felhasználó barátai különböző szálakban vannak, akkor csak akkor látják a felhasználó üzeneteit, ha frissítik a webhelyet.

Ideális esetben szeretnénk értesíteni más példányokat egy eseményről a felhasználó számára. Így biztosak lehetünk abban, hogy minden csatlakoztatott eszköz élő frissítéseket kap.

Egy megoldás

A többi szálat értesíthetjük a Redis közzétételi / előfizetési üzenetküldési paradigmájával (pubsub).

A Redis egy nyílt forráskódú (BSD-engedéllyel rendelkező) memória-adatszerkezet-tároló. Használható adatbázisként, gyorsítótárként és üzenetközvetítőként.

Ez azt jelenti, hogy a Redis segítségével eseményeket oszthatunk fel példányaink között.

Vegye figyelembe, hogy általában a teljes szerkezetet valószínűleg a Redis-ben tároljuk. Mivel azonban a struktúra nem sorozható és „életben kell tartani” a memóriában, egy részét minden példányon tárolni fogjuk.

Az áramlás

Gondoljunk most azon lépésekre, amelyekben a bejövő eseményeket kezeljük.

  1. Az üzenetnek nevezett esemény az egyik aljzatba érkezik - így nem kell minden lehetséges eseményt meghallgatnunk.
  2. Az esemény kezelőjének érvként átadott tárgyon belül megtalálhatjuk az esemény nevét. Például, sendMessage - .on ('üzenet', ({esemény}) => {}).
  3. Ha van egy névkezelő ehhez a névhez, akkor végrehajtjuk azt.
  4. A kezelő választ küldhet a küldésre.
  5. A feladás a válasz eseményt a Redis pubba továbbítja. Onnan mindegyik példánkba kibocsátódik.
  6. Mindegyik példány elküldi azt a socketsState-hez, biztosítva, hogy minden csatlakoztatott ügyfél megkapja az eseményt.

Bonyolultnak tűnik, tudom, de viszem magam.

Végrehajtás

Itt van a tároló, ahol a környezet készen áll, így nem kell mindent telepíteni és beállítani.

Először egy szervert állítunk fel az Express szolgáltatással.

Létrehozunk egy Express alkalmazást, HTTP szervert és init socket.

Most már a foglalatok hozzáadására tudunk összpontosítani.

A Socket.io szerverpéldányát átadjuk annak a funkciónak, amelybe beállítottuk a köztes eszközöket.

onAuth

Az onAuth funkció egyszerűen utánozza a hamis engedélyt. Esetünkben token alapú.

Személy szerint valószínűleg a JWT-vel helyettesítem azt a jövőben, de ezt semmilyen módon nem hajtják végre.

Most térjünk át az onConnection köztes szoftverre.

onConnection

Itt látjuk, hogy lekérjük a felhasználói azonosítót, amelyet az előző köztes szoftverben állítottunk be, és elmentjük a socketsState-be, ahol a kulcs az azonosító, az érték pedig a socket tömb.

Ezután meghallgatjuk az üzenet eseményét. A teljes logikánk erre épül - minden esemény, amelyet az előlap küld nekünk: üzenetnek fog szólni.

Az esemény nevét az argumentumobjektumba küldjük - a fentiek szerint.

rakodók

Mint láthatja az onConnection szolgáltatásban, különösen az üzenet esemény figyelőjében, az esemény neve alapján kezelőt keresünk.

Kezelőink egyszerűen olyan objektumok, amelyekben a kulcs az esemény neve, az érték pedig a függvény. Az események meghallgatására fogjuk használni, és ennek megfelelően reagálunk.

Ezenkívül később hozzá fogjuk adni a feladási funkciót, és azt használjuk az esemény eljuttatásához a példányok között.

SocketsState

Ismerjük államunk felületét, de még nem valósítottuk meg azt.

Módszereket egészítünk ki egy aljzat hozzáadásához és eltávolításához, valamint egy esemény kibocsátásához.

Az add függvény ellenőrzi, hogy az állam rendelkezik-e olyan tulajdonsággal, amely megegyezik a felhasználó azonosítójával. Ha ez a helyzet, akkor azt egyszerűen hozzáadjuk a már meglévő tömbünkhöz. Ellenkező esetben először új tömböt hozunk létre.

Az eltávolítás funkció azt is ellenőrzi, hogy az állam tulajdonságaiban van-e a felhasználó azonosítója. Ha nem, akkor semmit sem hoz. Egyébként kiszűri a tömböt, hogy eltávolítsa a foglalatot a tömbből. Ezután, ha a tömb üres, eltávolítja az állapotból, és a tulajdonságot meghatározatlanná teszi.

Redis kocsma

A pubsub létrehozásához a node-redis-pubsub nevű csomagot fogjuk használni.

Hozzáadás a feladáshoz

Rendben, most már csak annyit kell tennie, hogy hozzáadja a feladó funkciót ...

… És adjon hozzá egy hallgatót a kimenő_socket_ üzenethez. Ily módon minden egyes példány megkapja az eseményt, és elküldi azt a felhasználói aljzatokhoz.

Mindegyik többszálú

Végül tegyük hozzá a kódot, amely a szerverünk többszálú menetehez szükséges.

Megjegyzés: Meg kell ölnünk a portot, mert miután bezártuk a Nodemon folyamatunkat a Ctrl + c segítségével, ott csak ott lógunk.

Egy kis csípéssel most már működő foglalatokkal rendelkezünk az összes példányban. Ennek eredményeként: sokkal hatékonyabb szerver.

Nagyon köszönöm az olvasást!

Nagyra értékelem, hogy az egész kezdetben megdöbbentőnek és fárasztónak tűnhet, ha egyszerre összeszem. Ezt szem előtt tartva ösztönzőleg javasolom, hogy olvassa el újra a kódot, és egészében elgondolkozzon.

Ha bármilyen kérdése vagy észrevétele van, nyugodtan tedd őket az alábbi megjegyzés rovatba, vagy küldj nekem üzenetet.

Nézze meg a közösségi médiát!

Csatlakozzon a hírlevélhez!

Eredetileg a www.mcieslar.com weboldalon, 2018. szeptember 10-én tették közzé.