Kívül-belül JS

Azok, akik emberellenes bűnnek érzik a JavaScriptet, ne olvassák tovább, nekik ez a történet egy igazi horrorral érhet fel.

Jó ideje nézegetem a Node.js-t. Sok élménybeszámolót olvasgattam róla, hogy egy apache+php kombóhoz képest mekkora kánaán, sokkal több usert lehet vele kiszolgálni, stbstb.
Gondoltam, méregetek kicsit. Leszögezném, hogy a következő írást ne kezelje senki jó kis tutorialnak, mert nem erről szól. Nekem a programozás inkább hobbi, így a minőségén és a szakmaiságon lehet találni kivetni valókat.

Tehát fogtam a Nodejs hello word példáját, majd az általa kigenerált html-t betoltam az apache-ba is, oda már statikus html-ként.
Megtoltam mind a kettőt ab-vel és az eredmény tényleg hozta a okosabb emberek blogjain írtakat. Közel 5-6-szor nagyobb a kiszolgálható userek száma (pontos adatokkal tele a net, ha valakit érdekel).

Ezen felbuzdulva gondoltam egy merészet. Van egy régi webalkalmazásom, ami egy konverter. Gondoltam, megírom nodeban is, gyakorló példának jó lesz. Továbbvittem a perverzitást és a már régóta figyelt angularjs lett a választott kliensoldalra. Legalább ezt is megtanulom. Az eredeti alkalmazás szerveroldalon úgy néz ki, hogy html+php szokásos LAMP környezetben, egy C++ daemon pedig a tempbe lerakott infók alapján meghívja a szükséges konvertert. Kliensoldalon jquery dolgozgat, bekérdez, hogy kész vagyunk-e már, a felületet és az azon megjelenő státuszokat vezérelgeti. A kód baromira nem optimális (hiszen én írtam), régi cucc.

Az új verzióban alapvetően a node express keretrendszerét alkalmazom, így a route és egyéb nyalánkságok egyszerűbben megoldhatók. Jó pár modult kellett telepítenem, hogy ne az alapvető funkciók leprogramozásával menjen el az idő, de mint látom, ez a nodejs esetén koncepció. Annyiból tetszetős, hogy minden alkalmazás csak azt a modult éri el, ami neki rendeltetve van, míg egy általánosan kialakított hostingos LAMP-ban ami fent van, azt mindenki alkalmazhatja (okosabbak majd fixálják, ha hülyeséget írok).

Kliensoldalon az angularjs adta lehetőségeket kihasználva úgy döntöttem, hogy ha már mindenhol JS van, akkor a böngészőre bízom a renderelés nagy részét. Pl. nagyon jól alkalmazható a foreach, így a listákat, menüpontokat egy kapott json-ból vígan elő lehet állítani angularjs-el. Ezzel a szerver mentesül egy komplett html kiküldésétől, ergo nyertünk némi időt arra, hogy többen meglátogathassák az oldalt és a felület is interaktívabb, amolyan realtime érzésű. A fájlok feltöltésére az angularba számos modul behúzható, választottam ízlésnek megfelelően egyet.

Ez így megy is, nodejs oldalon viszont voltak nem egyértelmű dolgok. Millió leírás van a neten arról, hogy az express hogyan tudja a postolt fájlt feldolgozni, de nagy részük elavult (azóta változott kicsit az express), vagy ha aktuális, akkor egyszerűen nem működik. Újabb modul telepítése formidable személyében, így már remekül megy minden. Menne. Mivel a nodejs alapjáraton aszinkron, mint a javascript megoldások általában véve, nem igazán lehet ugyanazt a módszert követni egy mime ellenőrzéskor, mint mondjuk azt PHP esetén megszoktuk. Mire az ellenőrzésnek vége, a node addigra már rég közölte egy json segedelmével a userrel, hogy kész vagyok, aztán persze a tényleges eredmény még bőven nem állt rendelkezésre.
Tehát a felépítést újra kellett gondolni, gondolkodni a JS fejével. Jó kis agytorna, ha valaki csak a jquery animate megoldását használta ezelőtt :)

Megoldva ez is. Jelenleg itt tartok, a továbbiak még elég erősen gondolkodóba ejtenek:
- a konvertálás vezérlésére ha a biztonsági "szabályokat" nézem, akkor szintén kell valami különálló daemon. Viszont csábító az aszinkron működés is, amivel megoldhatóvá válna az, hogy maga a nodejs kód indítja el a konvertert, a user nem fog várakozni, timeoutolni, mivel egyidőben válaszolhatunk is neki, hogy vettük, csináljuk. Csak hát ugye egy binárist indítani közvetlen user interakcióra nem egy bölcs dolog (hátha valaki tud okosat mondani erre).
- a terv, hogy nem a user fog x másodpercenként bekérdezni, hogy kész van-e már a fájlom, hanem a nodejs egyik jóságát, a websocketet szeretném alkalmazni, így majd a szerver pusholja felé, ha változás történt (ergo megszabadulunk pár felesleges kéréstől. Ennek hatékonyságát még nem tudom felmérni, websocketes programot még nem írtam, ebben a témakörben is szívesen várom a tapasztalatokat).

A történet végén pedig az eredeti LAMP-ra épített és a node verzió mérései fognak megtörténni. Kíváncsi vagyok a végeredményre :)

Hozzászólások

Kis spinoff.

Csináltam egy nodejs alkalmazást, ami logot gyűjt (több forrásból). Abból számol és származtat, mindegy is igazából, a daemonnak mindig mennie kell.

Erre ráhúztam egy megjelenítőt, ami egy html + js combo, socket.io-val (az adatok áramlása kb. 1k/sec, tehát egy folyamatosan frissülő felületről van szó).

Amikor hallottam az angularról és pont újra kellett írnom az egészet, gondoltam egy merészet és kapásból azzal csináltam meg. Aztán rájöttem, hogy olyan szintű prociidő-pazarlás folyik, hogy ez nem mehet így tovább. Megkerültem az angulart és sima js-ben (+jquery) megoldottam mindent, a cpu használat az ötödére(!) esett vissza.

--
deejayy DOT hu

Sajnos nem ennyire egyszeru a dolog. Majd' 1 evnyi angular-ozas mellett kezdem el azt erezni, hogy mostmar kapargatom a feluletet es nem irok feltetlen rossz/lassu kodot. Rengeteget lehet optimalizalni egy angular alkalmazason - foleg ha listakrol, nagy adatmennyiseg megjelenitesrol van szo. Ha nagyon durvulni akar az ember, be lehet vonni a React.js-t is, ha olyan az app.

Hirtelen ajanlott olvasmanyok:
http://stackoverflow.com/questions/9682092/databinding-in-angularjs/969…
http://blog.scalyr.com/2013/10/31/angularjs-1200ms-to-35ms/
http://tech.small-improvements.com/2013/09/10/angularjs-performance-wit…

"Csak hát ugye egy binárist indítani közvetlen user interakcióra nem egy bölcs dolog (hátha valaki tud okosat mondani erre)." <= Szerintem a bináris process fusson alapból, és egy queue -ból vegye ki a feladatokat, amiket libasorban végre is hajt. Kell még valami "guardian" megoldás is, ha valamiért a bináris elhasal, újra legyen indítva, ne halljon le emiatt az egész oldal.

Egyébként +1 az egész postra.

"Azok, akik emberellenes bűnnek érzik a JavaScriptet, ne olvassák tovább"

Én pont olyan vagyok, de mégis elolvastam :-).

Mit csinál a node.js keretrendszeren futó webszerver? A kliensről jövő csomagot passzolja egy natív programnak, majd a választ visszapasszolja a kliensnek? Vagy végez valami transzformációt is az adaton?

A történelmi írások szerint a nodejs önmagában egy webszerver (a tervezője eredetileg egy aszinkron webszervert akart írni). Tehát a kérdés szerintem fordítottan lenne helyes, azaz hogy a mit csinál a nodejs webszerveren futó keretrendszer. De jelzem, nem vagyok benne még annyira, hogy mindent értsek én is, így szeretném, ha valaki látó ember ezt helyesbítené, vagy megerősítené.

Ja, értem :)
A tervek szerint azt fogja csinálni, hogy a feltöltött fájlt leteszi a diskre, bejegyzi egy átmeneti táblába, memcachedbe, akármibe, hogy mi a fájl neve, ad egy státuszkódot hozzá, hogy hol tart a folyamat (felkerüléskor kap egy nagy nullát). Ezt egy daemon felolvassa, ad neki egy 1-es státuszt, hogy megkezdte a munkát (ekkor el is kezdi konvertálni). Ha végzett, leteszi a diskre a kész fájlt és ad egy 2-es státuszt a fájlnak. Ha a user 2-es választ kap a kérésére, elkezdi letölteni a fájlt.
Kb. így működött az előző verzió is. A változás most igazából csak a technológián van, illetve hogy a státuszellenőrzést nem a kliens fogja végezni bekérdezéssel, hanem maga a nodejs és ő fog kiszólni a kliensnek.
Szóval ő maga nem csinál semmit az adattal (egyelőre).

Én sírok a JS-től szerver oldalon, de nagyon érdekesnek és tanulságosnak találtam a bejegyzésedet. Kerestem a neten olyan teszteket, ahol az apache statikus html kiszolgálásban alulmarad a node.js-sel szemben, de hirtelen nem sikerült ilyet találnom. Tudnál dobni egy linket?

Ugyanakkor ezt találtam: http://blog.metaobject.com/2011/01/nodejs-performance-httpd-performance…

Ami rámutat arra, hogy ha valaki veszi a fáradtságot és a szerver oldali aktív tartalmat inkább valami erősebb nyelven írja (egy ilyen kicsi beépített http szerver könyvtárral), akkor azért jelentősen gyorsabb lehet, mint a node.js. És ez szerintem egy tisztán JSON-t kiszolgáló szervernél simán megoldható. Persze ez nyilván akkor ésszerű, ha a szolgáltatás legalább egy saját virtuális gépen fut. Mellé tenni egy Apache-ot vagy más reverse proxy-t elég overkill :D

Melóban vagyok, itt most nem tudok neked prezentálni saját mérési eredményt, de Googlebe betoltam a "nodejs vs apache" szavakat (ezek is csak php-s mutatványok sajnos):
https://code.google.com/p/node-js-vs-apache-php-benchmark/wiki/Tests
http://zgadzaj.com/benchmarking-nodejs-basic-performance-tests-against-…

A második linken az összegzésben a nodera ezt írja: "Obviously it is more hungry for system's CPU and memory, but this should not be surprising considering its performance."
Nekem jellemzően fordított tapasztalatom volt, míg a nodejs 50 mega körül evett, addig az apache alaphangon 100 körül is képes volt. Load tekintetében szintén a node győzött nálam, alig mozdította meg a CPU-t (pedig a tesztalkalmazást még nem is többszálúsítottam), az apache képes volt totál beállni egy magasabb sűrűségű teszt során.
Nyilván az én tesztjeim nem voltak mélyrehatóak még, tehát fenntartom a tévedés jogát. Ha kész a komplett tanuló project, akkor azzal szintén fogok méregetni.

Csak egy gyors tesztre volt most időm.
A második linken található nodejs példafájlt betoltam (hello world), annak html kimenetét statikusan bedobtam apache-ba is. Ugyanazon szerveren mértem (nem localhostos a mérés, a szerver külföldön van fizikailag. A gép előtt egy haproxy is van, mivel ez egy virtuális masina.)

Apache: http://pastebin.com/5CXqq6g3
Node.JS: http://pastebin.com/Zytm6dwr

Szerintem itt lesz egy kis félreértés.
Szemmel láthatólag atya úgy értelmezte, hogy statikus html kiszolgálást hasonlítotok össze node.js -sel.
A link alatt pedig úgy látom nem erről van szó, php generálja le a hello world -öt. Nos, bár én erősen és határozottan szimpatizálok a node.js -sel, de azért ez még nem nagy eredmény, a php-nél tulajdonképpen bármi jobb.

Nekem a mérések alapján is úgy tűnik. Ami elég nagy szegénységi bizonyítvány az Apache-nak. Bár feltehetőleg a header parsing overhead adja ezt a különbséget és egy tipikus valós weblapnál (mondjuk 5-10k) már nem lenne ekkora a különbség. De ezt is érdemes lenne letesztelni. Esetleg lehet, hogy az Apache másik módban futtatva jobb eredményt tudna adni... Mindenesetre meg fogom nézni (és mérni) az NginX-et és a Lighthttpd-t, amikor majd lesz időm ilyesmire.

A ma délelőtti postomban az apache nem használ PHP-t. Amit a nodejs kigenerált html-t, azt egy az egyben bedobtam egy index.html-be és ezt szogáltam ki az apache-al. A pár napja talált két linknél pedig jeleztem, hogy az PHP-s, mert nem találtam hirtelen statikusra vonatkozó adatokat. Ezt pótoltam ma délelőtt, bár sok időm nem volt rá.