ugrás a tartalomhoz

Node.js filozófia

MadBence · 2011. Nov. 6. (V), 17.19
Előre is bocsánat, ha nem megfelelő a téma, de talán ide illik leginkább. (ha mégsem, akkor a szerkesztőkre bízom a dolgot)
Nemrég kezdtem el belemélyedni a node.js-be, és kezdem megszokni ezt az aszinkron működését, de pár dolog nem hagy nyugodni.
Alapvetően ezelőtt PHP-ban csináltam ezeket a netes dolgokat, azaz 1 http kérés = 1 php lefutás. Itt ugye a node-ban egyszer elindul az alkalmazás, és csak kiszolgálja a kéréseket, miközben rengeteg dolog, amit használ, benn van a memóriában. Így első körben az foglalkoztat, hogyha pl egy http lekérésnél beolvasok egy fájlt, azt minden lekérés alkalmával megtegyem-e, vagy valahogy érdemes lenne egyfajta cache-ben tárolni (nem gondolok itt bonyolult dologra, egyszerűen csak benn hagynám a memóriában, azaz nem valami kérésenként lokális változóban tárolnám az eredményt), ami adott esetben gondolom sokkal gyorsabb, mivel diszk i/o művelet nem is történne a tövábbi kéréseknél. Ugyanez vonatkozik az adatbázis lekérésekre is (mongodb).

A másik kérdés is az adatbázisra vonatkozik, nevezetesen hogy ha létrehozok egy kapcsolatot (node->mongo), akkor azt érdemes megtartani, vagy kvázi mint PHP->MySQL esetben, kérésenként felépíteni, lebontani. Gondolom teljesítmény szempontjából az előbbi megoldás előnyösebb, de nem tudom pl jó-e a kapcsolat szempontjából, hogy állandóan nyitva van (mi történik, ha egyszer csak egy hiba folytán megszakad?).

A válaszokat/tanácsokat előre is köszönöm :)
 
1

nem értek a node-hoz, de...

Crystal · 2011. Nov. 6. (V), 17.32
első körben ha egy konfig fájlt vagy ilyesmit beolvasol, akkor azt szerintem mindenképp érdemes mondjuk a node alkalmazásod indulásakor beolvasni, vagy lazy loading megoldással, de semmiképp sem minden kérésnél.

Az adatbázis cache-elés már kicsit problémásabb kérdés, mert egyrészt több alkalmazásos környezetben (azaz ugyanabban az adatbázisban több alkalmazás turkál) megszivathatnak a cache-be beragadt adatok, másrészt a mongo is cache-eli a gyakran használt adatokat, plusz alatta az oprendszer is cache-eli a gyakran használt fájlokat, szóval hogy érdemes-e ugyanazt az adatot egy harmadik memória cache-ben is tárolni, az kérdéses. Le kell mérni.

Az adatbáziskapcsolatot meg ha csak lehet egyszer építsd fel - csendesen teszem hozzá, hogy PHP-ben sem illik minden kérésnél újrakapcsolódni.
3

Utolsó mondat

MadBence · 2011. Nov. 6. (V), 17.58
Ezt az utolsó mondatot úgy érdemes érteni, hogy pl a mysql_connect helyett mysql_pconnectet kellene használni? (illetve egyiket sem, mióta van mysqli, de akkor a mysqli-t is perzisztens módban illene használni?)

Egy másik (bár még mindig a node működéséhez kapcsolódó téma), nevezetesen, hogy mivel minden aszinkron (vagy érdemes aszinkron módon működnie), hogy lehet eldönteni, hogy 'kész vagyunk-e'. Tehát indítok mondjuk 2 adatbázis kérést egyszerre, milyen módszerrel tudom megmondani, hogy kész van-e minden? (le lehet-e zárni a http kapcsolatot). Így intuitívan mindenféle flag-ek használatával bíbelődök, de szerintem kell lennie elegánsabb megoldásnak is.
4

Nem

Poetro · 2011. Nov. 6. (V), 18.14
A prezisztens kapcsolattal az a baj PHP esetén, hogy elő tud fordulni, hogy mindenféle hibák miatt (már nem tudom milyen módon kell a PHP-nak futni, hogy jól működjön, talán fast cgi) sok párhuzamos kapcsolat nyílik, és egyik se záródik be. Így előfordulhat, hogy elfogy a rendelkezésre álló kapcsolatok száma, pedig egyiket se használja semmi, csak meg lettek prezisztensen nyitva.

Mit értesz azon, hogy készen vagy-e? Ha összegyűjtöttél minden adatot, amit el akarsz küldeni a kliensnek, és el is küldted, akkor „készen vagyunk”. És mehet a kliensnek az response.end(). Az már nem érdekes, hogy a logolás, esetleg még valamilyen adatbázis beszúrás, módosítás még hátravan, mert jó eséllyel gyorsabban lezajlik, minthogy a kliens megkapja akár csak az első bájt adatot. Hogy pontosan akkor hajts végre egy függvényt, amikor minden aszinkron kérésed lezajlott, arra van pár nagyszerű módszer.
Ehhez használhatsz több flow control modult, amikről az előadásomban is beszéltem. Ilyenek az async, fibers vagy az egyik kedvencem a funk.

Node.js Windows környezetben
5

A kérdés bennem is felmerült:

Hidvégi Gábor · 2011. Nov. 6. (V), 19.01
A kérdés bennem is felmerült: honnan tudom, mikor vagyok kész? Példa: elindítok nyolc párhuzamos lekérdezést, mindegyik végeztével le kell ellenőriznem, hogy megtelt-e már a végeredményt tároló tömb? Ha mondjuk az egyik lekérdezés egy nagy adattömböt eredményez, aminek hosszú ideig tart a bemásolása a tárolóba, és közben megérkezik egy másik adattömb, amit ugyancsak elkezdünk bemásolni a tárolóba, nem fog gondot okozni? Hány lekérdezést indíthatok el párhuzamosan? Mi van azokkal a lekérdezésekkel, amelyeket még nem indíthatok el? Kell egy menedzsert írni, ami figyeli, hogy mikor ürült ki a sor?

Nagyon máshogy működik egy ilyen párhuzamos futtatásra képes rendszer, és egymillió kérdést felvet.
7

Párhuzamos

Poetro · 2011. Nov. 6. (V), 19.46
Ugye azt tudjuk, hogy általában mely lekérdezéseket futtathatjuk le párhuzamosan. És tudjuk, utána melyeket akarjuk lefuttatni. Az async használatával ez valahogy így nézne ki:
async.parallel({
    lekerdezes1: function(){ /* Lekérdezés 1 */ },
    lekerdezes2: function(){ /* Lekérdezés 2 */ },
    /* ... */
    lekerdezesn: function(){ /* Lekérdezés n */ },
}, function (err, results) {
  // Ez akkor fut le, amikor már mindegyik lefutott. 
  // A kérések párhuzamosan fognak futni.
  // A results-ban vannak az eredményeink az objektumban
  // megadott kulccsal.
  // Itt használhatjuk őket, vagy indíthatunk új lekérdezéseket.
});
Egymás utáni lekérdezésekhez pedig:
async.serial({
    lekerdezes1: function(){ /* Lekérdezés 1 */ },
    lekerdezes2: function(){ /* Lekérdezés 2 */ },
    /* ... */
    lekerdezesn: function(){ /* Lekérdezés n */ },
}, function (err, results) {
  // Ez akkor hívódik meg, amikor mind lefutott.
  // A kérések egymás után fognak lefutni.
  // A results-ban vannak az eredményeink az objektumban
  // megadott kulccsal.
  // Itt használhatjuk őket, vagy indíthatunk új lekérdezéseket.
});
Azt, hogy mennyit indíthatsz párhuzamosan egyértelműen megmondja az adatbázis-kezelőd. Ott be van állítva, mennyi kapcsolatot kezeljen párhuzamosan. Valamint használhatsz ugye egy pool-t, ahogy írtam, és abban tovább korlátozhatsz, hogy ha több alkalmazás használja párhuzamosan az adatbázist, akkor nekik is jusson még kapcsolat. A MySQL több tucat kapcsolattal is vígan futhat, azaz akár 10-20 párhuzamosan futó klienst is ki kell, hogy tudjon szolgálni.
6

Kösz, kipróbálom a linkelt

MadBence · 2011. Nov. 6. (V), 19.17
Kösz, kipróbálom a linkelt megoldásokat.
2

Cache

Poetro · 2011. Nov. 6. (V), 17.55
Van amit érdemes cache-ben tárolni, van amit nem. Az olyan fájlok, amik ritkán kerülnek használatra, nem érdemes cache-elni. Amiket gyakran használsz, azt az operációs rendszer úgyis cache-elni fogja. De vannak olyanok, amikre folyamatosan szükség van, például minden lekérdezéshez kell. Ilyenek lehetnek például template fájlok, amiket a megfelelő beállítás mellett a template kezelőd úgyis cache-elni fog. Más gyakran használt fájlok esetén esetleg érdemes memóriában cache-elni, de elég ritka, hogy vagy a Node, vagy az általad használt keretrendszer ne cache-elné a fájlt.

Adatbázis kapcsolatokhoz érdemesebb inkább egy pool-t használni, mint amilyen a generic-pool, node-mysql-pool, common-pool illetve mongo-pool. (A generic-pool-t azért is ajánlom, mert jó részét én írtam :) Ebben kezelni tudod azt az esetet, amikor a kapcsolat megszakad, és magától tud nyitni újakat, ha szükség van rájuk. A budapest.js előadásomban be is mutatom a generic-pool használatát, ugyan SQLite adatbázissal és csak maximum egy kapcsolódással, de a lényeg látszik belőle.