2010. október 29., péntek

retró

Arra még emlékeztek, hogy valamikor régen CD-n és DVD-n terjesztettek szoftvereket? Pár levitézlett harcost bemutatnék a képről: IBM DB2 és Websphere, Rational, SAP gányolmány nagy mennyiségekben (hogy szerettem a német rövidítéseket az abapban...), Novell féle suse, Sun One appszerver (6 CD!!!) A kupca alján van valahol pár Oracle motyó is, tudom hogy valahol van benne Sybase is. Na meg a hardware-k kidobált windowsos driver CD-i. A microsoft termékek hiányoznak totál szőröstül bőröstül, mert azt itthon nem használok.

Szakmai önéletrajz helyett, mondjuk úgy 5-6 évvel ezelöttig :-)

2010. október 28., csütörtök

ws://

A héten kerítettem időt magamnak egy pár prototipus fejlesztésre és kifejezetten érdekelt a html5 (ezzel kapcsolatban szólt ki ugye nemrég a w3c-s csákó, hogy még sehol sem production quality, ezt egyébként tapasztaltam is) websocket. Amit összetúrtam, az tényleg csak a prototipus építés összegzése, ne vegyétek valami szakértői véleménynek :-)
Szóval nézzük mi ez az egész...

A hiper-szuper interaktív szines-szagos weblapok fejlesztésénél a jó öreg request-response megoldás már nem nyerő. A websocket tulajdonképpen egy socket szerű dolog. Küldözgethetsz rajta mindkét irányba üzeneteket, ahogy tetszik. Azt a problémát akarja megoldani, mint az adobe RTMP nevű szőrös gorillája. Konkrétan az is tud utazni http felett, de hát ezt a dolgot jobb nem eröltetni, ugyanis mocskosul lassú.

Nos hogy a rákba fér bele a socket-szerű működés a http protokolba? Nem egyszerűen. Eredetileg úgy volt, hogy a ws protokol nem a 80-as porton fog utazni, hanem 81-es. Aztán ezt az ötletet elvetették, rettegve a firewalloktól és a proxyktól, most ott tart az ötlet - és úgy tűnik ezen már nem változtatnak - hogy ez is a 80-as porton fog utazni. A wikipédia cikk tök jól leírja, hogy fog ez működni. Nem is tudom használták-e valamire idáig a http 101 státusz kódot.

Browser háború

A wikipédia cikk a mainstream broesereket is felsorolja, csak annyit jegyeznék meg ezzel kapcsolatban, hogy jelenleg a chromium az egyetlen browser, ami kint van mainstream elérhető több oprendszerre web socket támogatással. Annak mennyi a piaci részesedése? Nem sok. A Internet exploiter 9 tartalmaz majd websocket támogatást, ez jelenleg testdrive.
Szóval erről az oldaláról a dolog szerintem jelenleg nem bevethető állapotú.

Szerver háború

Aki a szerver oldalt akarja bütykülni, annak se lesz egyszerű dolga még egy jó ideig, a java servlet ugyanis semmi támogatást nem ad az egészhez. Két dolgot találtam a weben, amit fel lehet használni:

  • jwebsocket hát vele az a baj, hogy a protokolt implementálja kivállóan, csak nem fér rá a http portra mellé. Szóval muszáj neki alternatív portot meghatározni. Nem tudom mekkora gubanc a firewallok használata ebből a szempontból, pl már dolgoztam olyan helyen, ahol nem engedték ki az ismeretlen protokolokat. Pl ssh-val se lehetett kimenni. Hájli szikjúr... volt, amíg fel nem találták a pendrive-ot.
  • A jetty-s srácok csináltak egy nagyon szép kis apit a websocket kezelésére. És még ráadásul működik is, csodaszép és egyszerű. Egy baja van: totál jetty specifikus. Szóval hozzáberhelted az alkalmazásod a jetty containerhez. Amíg ki nem jön valami spec, addig jóban rosszban együt.
Etc háború

Hát az egészet azért bonyolították meg, hogy mehessen a 80-as porton. És megy is, de hogy ez mennyire van tesztelve az infrastruktúra többi részén... ilyenekre gondolok:
  1. proxyk kifejezetten
  2. http load ballancerek
  3. firewall-ok
Szóval hogy ez mind boldog lesz vajon? Annyi már most biztosnak tűnik, hogy a servlet 3.0-nál is jobban át kell majd rendezni az architektúrát.

Kérdések

Ha egyszer ez végre teljesen oké lesz és működik, akkor vajon
  1. Volt-e értelme a servlet 3.0-nak? :) Mert akkor csak úgy áttolnánk minden lassú interakciót websocketen, majd szól a szerver ha kész. (mondjuk a servlet 3.0 már itt van, a websocket meg mint kiderült még sehol)
  2. Ez az ajax hívások túlnyomó részét át fogja venni? Amit cachelünk, azt gondolom jobb lesz mégis a régi módszerrel küldeni.
  3. Nyugdíjba megy az ajax push? Nem fog hiányozni! :-)
  4. Például a socketek kiszolgálását hogyan lehet majd loadballancelni? Egész végig 1 ugyanaz a szerver fogja kiszolgálni a socketet?
Hát ennyit túrtam fel, azt hiszem most még kicsit rugdosom de hagyom érni még egy ideig. Azért ennek a technológiának a bevetéséhez igen alaposan át kell majd forgatnunk pár dolgot, egy ideig attól tartok el fog tartani, és még sehol sem tartunk vele.

2010. október 19., kedd

Az indiánok üldöztetése

Egyik nap apache benchmarkkal méregettem az egyik webappom válaszidejét és terhelhetőségét és hát nem voltam igazán boldog. Gondoltam, hogy már nem sok mindent lehet húzni rajta, sima adatbázis cókmókot pakol bele xml-be oszt jónapot, az egész architektúra a dögunalomig menően szokványos.

A tuningolás egyébként izgalmas téma, ezer dologgal lehet húzni egy ilyen rendszert, adatbázis optimalizálás, cache réteg kidolgozása, a DAO réteg optimalizálása, profile-olás, garbage collector tuningolás, memóriaparaméterezés, jdbc connection pool buhera, threadpool buhera... és mindezen már túl voltam és az egész még mindig nem muzsikált úgy ahogy nekem tetszik. Azért éreztem nyekergésnek az egészet, mert ugyanezen a vason lemértem hogy a default apache installáció statikus html tartalommal mit tud kezdeni. Rendesen odacsűrt a procinak, de 5000 req/sec körül teljesített. Ez nyilván egy unfair összehasonlítás, mert az apacsnak semmi mást nem kellett csinálnia, csak kipumpálnia egy file-t a tcp socketen. Ez adta az ötletet, hogy ha az én rendszeremnek csak ennyit kellene csinálnia, akkor megcsíphetné az apacs tempóját.

És ezt a receptet főztem ki:

  1. Végy egy filtert. Nevezd el ResponseCacheFilternek
  2. Végy egy JCache (ez az oldal kiválló példája egy halott JCR-nek) implementációt, de ha nincs vagy bizonytalan vagy, jó lesz egy HashMap is, lehetőleg legalább az adatokat Ref-fel csomagold hogy OOM-et azért mégse okozzon
  3. Fogd meg a HttpServletResponse-t és gondosan tekerd be egy HttpServletResponseWrapper-be. Itt van néhány trükk amit csinálni kell, de a lényeg hogy írj egy olyan ServletOutputStream-et, ami hasonlóan a apache commons io TeeOutputStream-jéhez (esetleg építhetsz is erre az osztályra) másolatot készít a kiírt adatból. Szerintem célszerű egy ByteArrayOutputStream-et használni ha az eredmény nem véresen nagy. Esetleg csinálhatsz köré egy limitált verziót, ami egy limit után már nem ír (mint pl a CountingInputstream)
  4. A filtered egyszerűen csak dobja tovább a végrehajtást az adatbázisba matató kódnak, ami majd mégtovább dobja az adatbázisból kiberhelt adatokat kiszerializáló kódnak, satöbbi. A végén valahogy a vezérlés visszakerül a filterhez.
  5. No ekkor kapjuk el a HttpServletResponseWrapperünk grabancát és követeljük tőle servlet output stream-re kiírt adatok másolatát, ez egy plain byte tömb ha minden igaz. Egyszerűen csak csináljunk egy kulcsot a request paramétereiből, és az értékként használva a response másolatát dobjuk be a cache-be
  6. A következő requestek beérkezésekor a requestből összebütykölt kulccsal megnézheted, hogy van-e cachelt válasz, ha nincs lásd fenti pontok, ha pedig van, akkor boldogság, mert csak egy byte tömböt kell kidobnod.
  7. Ezt a filtert applikáld rá a tipikusan csakis olvasást végző műveleteidre. Pl getCustomer, getLatestPosts, stb.
  8. Lesz még itt bonyolítás: a módosítást végző kód kell hogy kérni tudja a fenti response cache egyes elemeinek kiürítését. Ide clusteres esetben szerintem egy jms topicon érdemes körbeküldeni egy ID-t vagy hasonlót, ha nincs igény culsterezésre, elég csak direktben törölgetni kulcsokat a cache-ből. (erre is lehet kultúráltnak látszó kódot írni, akármilyen bunkón is hangzik)
  9. Kész! Elő a benchmark eszközzel!
Csak ennyivel még nem sikerült megszorongatni az apacsot, bár a teljesítmény igen látványosan megnőtt, de még most jön egy desszert.

A browserek (söt talán már az internet explorer is) képesek tömörített tartalmat kezelni. Az "Accept-Encoding" headerben küldik meg, hogy ők mit fogadnak el. Na ezt ki lehet használni. Általában azt szokták mondani, hogy hülye dolog generált tartalmat is gzippelni, mert sok időt visz. Talán így van, de ha csak egyszer generáljuk a tartalmat, és az eredményt becacheljük, akkor onnantól már nem kell újra és újra gzipelnünk a generált tartalmat. Ez lényegesen csökkenti  a hálózati forgalmat. Egy gzippelt response törtrésze az eredeti tartalomnak. Példaként a jól ismert JQuery minified 76 K gzippelve csak 26 K. Ez most egy rossz példa volt, mert az alig a harmada, de az én teszt kimeneteim harmincadukra estek össze és ez tényleg sokat dobott rajta.

Ez az egész csak akkor hatásos, ha az alkalmazás az olvasások számához képest ritkán módisítja a kimenetet ÉS a pillanatnyi pontos állapot nem fontosabb, mint a jó válaszidő. A legtöbb webapp tipikusan ilyen szerintem. Az internet ilyen. Egy bank esetében atomkatasztrófával érne fel, de speciel egy blogger cikk esetében nem nagyon érdekel senkit sem, hogy publish után még akár fél másodpercig is a régi tartalom jelenik meg másoknak.

Itt most egy kicsit kénytelen vagyok ködösíteni, mert mégiscsak a konkrét alkalmazáson múlik, de a cucc teljesítménye így vetekedhet, söt verheti is az apache statikus oldalak sebességét -ami mégegyszer nagyon unfair összehasonlítás, hiszen az apacsot nem tuningoltuk és esetleg csinál egy rakás egyebet amit a jetty vagy tomcat nem, és fordítva.
A válaszidőkről még annyit, hogy amikor egy-egy kulcs (vagy egyszerre több is) kiesik a cache-ből TTL, GC vagy a fenti kódolt mechanizmus által, újra a régi és lényegesen lassabbnak mért kódunk lép működésbe és ilyenkor amíg a cache-ben meg nem jelenik újra az eredmény esetleg több kérés is a háttérlogika végrehajtásához kezd, ami például tökönrúgja az adatbázist. Egy mérésben ez meg is fog látszani, tipikus bemélyedések szabályos időközönként.
Már erre is volt megoldásom egyébként, csak még nem építettem bele ebbe a response cache megoldásba, nagyjáből annyi, hogy háttérszálon is frissítheted az eredményt, ha nem borzasztó sürgős és kritikus fontosságú mindig pontos eredményt adni, és addig visszaadhatod a régi eredményt. Persze ha a cache-d nem dobja ki a régi eredményt. Szóval ez egy kicsit gubancosabb történet, de végig lehet járni ha valaki ki akarja kalapálni a csorbákat a performace görbéjén.

Na ennyi, remélem valamennyire tanulságos volt. A francba kedd lett közben, akkor jöjjön még egy ilyen a teljesítmény tuningoláshoz:

Egyik nap azt találom ki
Hogy másnap százon én nyerek
És nő hegyek vesznek körül
De én nem szeretek egyet se
Még párszor győzök
De már csak megszokásból

2010. október 13., szerda

JaSON is EVAL...

Tegnap próbálgattam JQuery 1.4-gyel meghívni egy kézzel hímzett JSON-t gyártó ajax backendet és nagyon furcsa eredményeket kaptam. Egész pontosan a callback metódus nem került meghívásra, és pedig azért, mert nem tudta felparsoloni a json kimenetet. Néztük ketten is, hogy hol lehet a gubanc a kimenetben, és mint kiderült az idézőjelekkel volt a baj. Egyszeres idézőjel nem jó, dupla idézőjel jó.

A dolog egyébként azért volt meglepetés, mert nyilván erre a kézzel hímzett json kimenetre is írtunk validációt és a jávás json parser átengedte simán, mint ahogy a JQuery 1.3 is. Nekem kicsit a régi CORBA idők jutottak eszembe róla. Bár a probléma gyökere más, ami a felszinen van, az mégiscsak rémesen hasonlít :-(