Long-polling terheléscsökkentés
Sziasztok!
Adott egy weboldal, ami long-pollinggal kérdez le információt a szerverről (pl. az új levelek, értesítések számát). Ha a felhasználó sok lapot nyit meg, akkor indokolatlanul sok párhuzamos kérést küld a szerver felé, miközben mindegyik lap ugyanazt a választ kapja meg - tehát elég lenne, ha egyszerre csak egy lapon történne AJAX-hívás.
A lekérések párhuzamosságát wildcard DNS-bejegyzés (*.example.com) biztosítja, így mindegyik egy véletlenszerű aldomainre mutat, tehát a böngésző nem korlátozza a párhuzamos lekéréseket.
Kérdésem, hogy szerintetek mi lenne a legjobb megoldás, hogy ilyen esetekben csökkenthessük a terhelést? A lapok egymás közti kommunikációjához süti, LocalStorage vagy SessionStorage merülhet fel (bár csak az előbbi kompatibilis a régebbi böngészőkkel). Egy lekérés küldésekor a szkript menthetné a böngészőben, hogy mikor indította a lekérést, és egy másik lapon csak akkor indul lekérés, ha ez régebben történt, mint a long-polling maximális hossza (sikernél vagy sikertelenségnél pedig törlődik az időbélyeg). Ha a felhasználó az éppen lekérést indító lapfület zárja be, akkor itt lehet kiesés, de a szkript akár másodpercenként is felülírhatja az időbélyeget, így ez kiküszöbölhető. A szervertől kapott válasz pedig szintén mentésre kerülne sütibe vagy SessionStorage-be, így az inaktív lapok ennek aktuális értékére támaszkodnának.
A felhasználónkénti terhelés csökkentése nélkül, ha mindenki megnyit 10 lapot, akkor néhány tíz látogató egyidejű bejelentkezése esetén már el is érnénk a szerver maximális párhuzamos kiszolgálási kapacitását.
Mit gondoltok?
Válaszotokat előre is köszönöm,
Ádám
■ Adott egy weboldal, ami long-pollinggal kérdez le információt a szerverről (pl. az új levelek, értesítések számát). Ha a felhasználó sok lapot nyit meg, akkor indokolatlanul sok párhuzamos kérést küld a szerver felé, miközben mindegyik lap ugyanazt a választ kapja meg - tehát elég lenne, ha egyszerre csak egy lapon történne AJAX-hívás.
A lekérések párhuzamosságát wildcard DNS-bejegyzés (*.example.com) biztosítja, így mindegyik egy véletlenszerű aldomainre mutat, tehát a böngésző nem korlátozza a párhuzamos lekéréseket.
Kérdésem, hogy szerintetek mi lenne a legjobb megoldás, hogy ilyen esetekben csökkenthessük a terhelést? A lapok egymás közti kommunikációjához süti, LocalStorage vagy SessionStorage merülhet fel (bár csak az előbbi kompatibilis a régebbi böngészőkkel). Egy lekérés küldésekor a szkript menthetné a böngészőben, hogy mikor indította a lekérést, és egy másik lapon csak akkor indul lekérés, ha ez régebben történt, mint a long-polling maximális hossza (sikernél vagy sikertelenségnél pedig törlődik az időbélyeg). Ha a felhasználó az éppen lekérést indító lapfület zárja be, akkor itt lehet kiesés, de a szkript akár másodpercenként is felülírhatja az időbélyeget, így ez kiküszöbölhető. A szervertől kapott válasz pedig szintén mentésre kerülne sütibe vagy SessionStorage-be, így az inaktív lapok ennek aktuális értékére támaszkodnának.
A felhasználónkénti terhelés csökkentése nélkül, ha mindenki megnyit 10 lapot, akkor néhány tíz látogató egyidejű bejelentkezése esetén már el is érnénk a szerver maximális párhuzamos kiszolgálási kapacitását.
Mit gondoltok?
Válaszotokat előre is köszönöm,
Ádám
Szerintem találj ki egy
Nem csak a tárolás a probléma itt szerintem, hogyan döntöd el több lapnál, hogy már van e a localstore-hoz hozzárendelve long pollingos szinkronizáló, vagy sem? Pl ha valaki bezárja a lapot, amin az aktív szinkronizáló volt, akkor leáll a szinkronizálás. Erre is ki kéne találni valamit szemafort vagy ilyesmit, és folyamatosan kéregetni az erőforrást. Az erőforrás kiadását meg kb fogalmam sincs mi alapján lehetne eldönteni. :S Max a legutóbbi sikeres kérés időpontját lehetne lementeni, és ha már régen volt, akkor kiadni az erőforrást, de ilyenkor is előfordulhat, hogy két lap párhuzamosan kapja meg... Lehet még onbeforeclose-ra kötni az erőforrás felszabadítását, stb... Nehéz kérdés, sosem foglalkoztam vele...
IO loop
Ugy latom, hogy nem a feladat van leirva, hanem igazabol egy konkret implementacio menten gondolkodsz. Ha ugy egyszerubb, csorogj fel valamikor Skype-on, mert ez tipikusan mar atmegy az architektura-tervezes kerdeskorebe.
A socket.io-t én is tudom
Arra gondoltam, hogy vagy localstore-ban valahogy le kellene tárolni, hogy ki van osztva a szinkronizáló erőforrás, és tab bezáráskor felszabadítani, vagy ha már ki van osztva, akkor valahogyan megakadályozni, hogy újat nyisson egy másik tab, mondjuk ha egy domain-re csak egy kapcsolat lehet egyszerre nyitva, akkor a kapcsolatok limitálása pont jó lenne ilyen szempontból. A gond csak az, hogyha jól tudom egyszerre 4 vagy több kérés is mehet egyetlen domain-re. Hogy is van ez?
Körvonalazódik
1.) Ha van, használja az ottani értéket.
2.) Ha nincs, elindítja az AJAX-kérést, és a válaszig másodpercenként frissíti a timestampet.
A szinkronizálás időtartamára
onunload
eseménykezelőt kap az ablak (köszönöm, inf3rno, ez jó irány), ami törli a timestampet.Mindegyik oldal pedig másodpercenként ellenőrzi az időbélyeget, és annak függvényében cselekszik.
Szerintetek?
A socket.io és a NodeJS virtuális post-iteken lebeg már jó ideje az arcom előtt, vastag piros betűkkel felírva :) Janoszen, köszönöm a lehetőséget – ha kicsit belekóstoltam már ezekbe, élni fogok vele.
Egyszerre 2-8 kérést engednek egy domainre a böngészők, emiatt álltunk rá a Facebookhoz hasonlóan a random hostokra – igaz, a session egy darabig beleszólt a párhuzamosságba a lockolással, de a gyors
session_write_close()
ezt is rövidre zárta :) A limitálás egyébként azért sem lenne szerencsés, mert az csak késlelteti a kérést, illetve többféle adat lekérése is felmerülhet, amit muszáj párhuzamosan kiszolgálni.A 10k kliens egyidejű lekéréseit az adatbázis-szerver is ilyen vidáman fogadja?
Nem
Ami a NodeJS-t illeti, az otlet az egeszben az, hogy ha valami valtozik, akkor az adatbazis megkerulesevel direktben meglokod a NodeJS-t. Nyilvan lesz legalabb egy komponens aki tud a valtozasrol, hiszen valami beleirja a DB-be is. Tehat nem kerdezgetsz folyamatosan, hanem egy valtozas hatasara (Event vagy Edge) kuldesz uzenetet. Mivel a passziv varakozas nem eszik CPU-t, ez egeszen addig jol megy, amig bele nem futsz valami kernel limitbe, ami jellemzoen a C10K problema, azaz a 10e + connection problemaja.
A nodejs esetében a 10k
Átgondoltam a dolgot, ha
A localStore-al kapcsolatos megállapításaim nem biztos, hogy igazak, nem használtam eddig localStore-t, de kb ilyen fapados adatbázisnak képzelem el, amiben nincs tranazkció kezelés, nincsenek triggerek, nincs állandó kapcsolat a tabok felé, nem tud saját függvényeket futtatni, stb... Nem hiszem, hogy ennél komolyabb lenne, de utána kell nézni... Annyi extra lehetőség lehet benne, hogy js függvényeket le lehet tárolni benne, és utána a tabokban egyesével lefuttatni, szóval nem muszáj mindent string-ben tárolni, és minden tab-ba betenni a logikáját... Elég lenne csak a socket.io-s tab-bal csomagoltatni, a többivel meg egy az egyben lefuttatni... Szerintem erre a long-pollingos localstore elérésre lehetnek kész pluginek kidolgozva, szal felesleges sajátot írni, inkább keresni kell.
szerk:
Úgy nézem, hogy van StorageEvent, ami gyakorlatilag a triggernek felel meg, és amin keresztül elméletileg jelezni lehet minden tab-nak, hogy változott valami az adatbázisban. Így viszont nem feltétlen kell longpollingozni, ha az onbeforeunload működik a böngészőben, akkor arra rá lehet kötni egy StorageEvent-et, és értesíteni minden tabot, hogy felszabadult a socket.io-s erőforrás, amivel nodejs-től kéred le az adatot. Ha nincs localstore, akkor sajnos marad a longpolling-os megoldás cookie, vagy bármi egyéb felé, de akkor már szerintem jobb inkább minden tab-nál külön socket-et nyitni a szerver felé, kevesebb a befektetett munka. Teljesen attól függenek a lehetőségek, hogy milyen felhasználói rétegre tervezel, és azoknak milyenek a böngészőik...
szerk2:
http://blog.fastmail.fm/2012/11/26/inter-tab-communication-using-local-storage/
Itt írnak long pollingos localstore kommunikációról. Amit persze itt nem long pollingnak neveznek, de mindegy, szóval nem közvetlen event-es, hanem pingelős...
A storage event témának még jobban utána kell néznem.
Ami még esetleg szóba jöhet az a shared webworkers, amiről eddig nem sokat találtam, de elvileg jó ilyesmire... Annak is utána kell néznem...
Szerver
Ja, de pont arról volt szó,
Az igazi az lenne, ha egy webworkert lehetne indítani úgy, hogy ahhoz minden kérdéses tab hozzáfér, és csak akkor állna le, ha az összes tab bezárult. Nem tudom, hogy erre mennyire van lehetőség, szerintem semennyire, de utánajárok. Egyelőre az a benyomásom, hogy a webworkerek csak a nyitó ablakukkal tudnak kommunikálni, és annak bezárultakor elvesznek.
IO
Nem igazán értem, hogy mire
Pl
A programozó kezében van a
Ertelmetlen
Kompatibilitással nincs
Úgyhogy ja, marad a php cseréje nodejs-re, vagy tornadora, vagy bármi hasonlóra, kinek melyik nyelv a szimpatikus...
Ha beteszem egy setTimeout-ba
Pl arra, hogy a localStore
Ez ebben a formában nem igaz, a localStorage-t lapbetöltéskor memóriába tölti minden böngésző, és gyakorlatilag azonnali az írás/olvasás, az első beolvasás az, ami blokkolhat. NC Zakas írt egy jó összefoglaló cikket a témában, aminek a végkövetkeztetése az, hogy ésszel használva nem okoz problémákat. (Ld. még: localStorage: Use it, Don't Abuse It)
Van még a WebSQL, ami viszont
LocalStorage-nél a szinkron működés szerintem is csak nagyobb adatmennyiségnél okozhat komolyabb akadást. Ha ésszel használják, és nem akarják a teljes adatbázist localStorage-be tenni, akkor nem okozhat gondot...
A böngészők egyik fele a
Passz, én már feladtam :D
http://jaydata.org/
Ha minden long pollinghoz
Én arra jutottam, hogy
Itt összeszedtem mindent, ami használható a témában:
http://stackoverflow.com/questions/19125823/how-is-it-possible-to-share-single-js-resource-between-browser-tabs/19165781#19165781
Jelenleg, amit javasolni tudok, és ami a legkevesebb erőfeszítéssel jár az az: intercom.js. Most kérdezgetem őket, hogy csak socket.io-t lehet e megosztani vele, vagy van e lehetőség long pollingra. Ha nagyon böngésző kompatibilis akarsz lenne, akkor építsd fel a saját rendeszered ez alapján, és használj cookie-t a BNC Connectorral.
Ha abból indulsz ki, hogy egy
Ha ez nem kielégítő megoldás, akkor az értesítéseket küldheted nyugodtan az aktív ablakba, pl. az alján lehetne egy állapotsor, ahova kiírhatnád, hogy az egyébb nyitott tabjain van-e változás. Ha van, átvált, az pedig automatikusan frissít.
Ez utóbbi amiatt is célszerű, mert a másik tabokon csak úgy tudod jelezni a változást, hogy az oldal title-jét írod át, amit vagy érszrevesz vagy nem (pl. sok tabot nyit meg egyszerre, és nem férnek ki egy képernyőre, mert a böngészőt így csinálták meg). Az állapotsávon például egy csodálatos animációval fel tudod hívni a változásra a figyelmet.
Kód
A kérések szerveroldali kezelése nem long-polling, mert az PHP-vel hosszabb időre lefoglalna egy szálat, ami nem túl gazdaságos, a későbbiekben ezt socket.io-val tudjuk tovább optimalizálni, de egyelőre 1000 online felhasználó esetén ez másodpercenként 100 lekérés lenne, aminek elviekben nem szabadna gondot okozni. Ennél több user mellett pedig már bőven bele kell férnie az infrastruktúra bővítésének. (Lévén egy Apache szerver úgyis csak 200-400 párhuzamos kérést tud fogadni.)
Még felmerült ötletként, hogy Memcached-ben tárolnánk a bejelentkezett felhasználókat érintő értesítéseket, ami cronból frissülne, de ezzel alapvetően csak az adatbázisra jutó terhelést csökkentenénk. Érdemes ezzel foglalkozni?
Ha nincs sok adat egyszerre,
Az adatbázissal csak akkor foglalkozz szerintem, ha kiderül, hogy az a szűk keresztmetszet valamilyen téren. Addig felesleges.
Storage event
Az IE – egyedüliként – az érték megváltozása előtt küldi az eventet, tehát egy rövid
setTimeout
kell az új érték lekérdezéséhez.A Chrome-ban pedig van egy lassan 3 éve (!) jegyzett bug: a
document.domain
módosítása esetén (aldomainről a root domainre) nem működik a localStorage eseménykezelője, tehát ilyen esetben rendszeresen le kell kérdezgetni az adott kulcs értékét.Ezért gyűlölöm a kliens
Van-e ertelme?
Részben