A következő címkéjű bejegyzések mutatása: servlet. Összes bejegyzés megjelenítése
A következő címkéjű bejegyzések mutatása: servlet. Összes bejegyzés megjelenítése

2010. november 28., vasárnap

Annotation hell

Gyorsan még az is eszembe jutott, hogy elmagyarázzam, miért gondolom azt, hogy az annotációkkal átestünk a ló túloldalára. Úgyis mindjárt reggel van, már jár a villamos.

A nagy XML konfigurációk helyett csodálatos dolog annotációt használni, amikor úgyis csak az az egy konfiguráció lett volna értelmes. Például JPA és más ORM annotációk. Még soha olyat nem láttam, hogy meggondolod magad és nem jó az a név annak a táblának. Kutyát nem érdekelnek a táblák nevei. Persze konkrétan a JPA lehetőséget ad arra, hogy felülbíráld XML konfigurációval az annotációkat.
Hasonlóan lelkesedek azokért az egyszerűsítésekért, amiket a JSR-181 REST annotációk, a JAXB annotációk ésatöbbi hoztak. Az így felannotált osztályokat és interfaceket az ember tipikusan egy pacakge-be dobálja. Igen, helyenként kicsit cifrán néznek ki az osztály-, metódus- és paraméterannotációk. Sebaj, az XML se lett volna kevesebb.


A másik esetben pont a szétszórtsággal van a probléma. Például a non intrusive IoC-nek pont az volt az előnye, hogy nem összeintegrált komponenseket gyártsunk, elég ha POJO, ennek megfelelően tradícionálisan szét lettek szórva a service osztályok, és nincs is nagyon remény a rendszerezésükre. Szóval ezért nem szeretem az annotált IoC-t, beleértve a spring annotációkat is, és tartok tőle hamarosan a servlet 3.0 annotációival is lesz problémám. Képzeld el így a problémát: átveszel valakitől egy spring annotált projektet, nem a dokumnetációtól működik a szoftver tehát dokumentáció nincs, a fejlesztőtől se várhatsz segítséget. Melyik projectet látod át hamarabb? Szerintem az XML-hell egy-két helyen jobb abból a szempontból ott legalább minden egy helyen van. Nyilván ettől lett hosszú. Attól, hogy szétkentük az XML file tartalmát csillió osztályba, attól nem lett se kevesebb, se átláthatóbb, csak az XML lett rövidebb. Sovány vígasz.

Log

Ismétlés a tudásnak azannnya. Péntek délután az Egér Miklós Vasművekben beszámolót tartottam a websüketes túrkálásaimról és a servlet 3.0-ról. Nem sok új ahhoz képest amit már egyébként itt is leírtam pár postban és ott is elmondtam:

  1. A Servlet 3.0 zseniális lépés a java szerver oldali technológiában. Na jó ez kicsit túlzás, de végre egy nagyon lényeges újdonság, ami komolyan növeli a java szerverek teherbírását azáltal, hogy egy hosszú és esetleg nem igazán CPU igényes kiszolgálásnak nem kell feltétlenül külön szálat fentartani. Adatbázis műveletek, JMS üzenetre várakozás, de például akár várakozás más erőforrásokra is, pl gondolom nem igazán szeretnél szerver oldalon párhuzamosan 10 db jóminőségű hubble űrteleszkóp képet feldolgozni kicsi heap memóriával. Ugye...
  2. A Servlet 3.0 kifejezetten olyan dolgokra nagyon alkalmas, mint a long poll. Amit ugyebár a szerver push-kén használunk.
  3. A Websocket API, ami már ott van a firefox 4-ben, chrome 7, IE 9 (jajj hát ki legyen mindig a legutolsó) satöbbi, szóval ez az API lehetővé teszi, hogy TCP socket-szerű tartós kapcsolatot építsünk fel a kliens és a http szerver között.
  4. A Websocket API ha egyszer elterjed, komoly átalakítsok vállnak kézenfekvővé a szoftverfejlesztésben. Szerintem a legtöbb esetben maradnak a régi ajax hívások is, de jópár esetben a websocket le fogja helyettesíteni. A long poll biztos hogy menni fog.
  5. Ennél vlószinűleg lényegesebb kihívásokkal kell az üzemeltetés terén szembenézni. A Websocket ugyebár HTTP 101 "Switching Protocol" státusszal kezdődik és innentől az egész kommunikáció nagyon más, mint egy szokásos HTTP kapcsolat során. Kell számítani nehézségekre a proxykon, load ballancereken, stb. Azóta kipróbáltam, hogy apache mod_proxy korrekten kezeli-e ezt a protokolt, de nem ment vele. Aztán lehet az apacs mágusok életre tudják kelteni, de nekem nem ment. Még ki kell próbálnom mod_ajp-vel is, valamiért én inkáb azt használom ha muszáj, nekem az egyszerűbb.
    Viszont a másik kérdés: akarunk apacsot az egész elé? Amikor az kapcsolatonként 1 processzt de legalábbis egy szálat visz, míg a java szerver oldalon már nincs erre szükségünk? Lehet a httpd bottleneck lesz az architektúrában? Illetve most még nem az?
  6. Még mindig kérdés, hogy szükség lesz-e a websocket témogatásához valami külön servlet engine kiegészítéshez, vagy belefér-e a sima servlet API-ba. Szerintem kifejezetten jobb lenne, ha a servlet API fürgén előjönne valami támogatással, én mindenesetre a jetty saját apiját használom. Ebből következőleg a kód nem fut pl tomcaten.
  7. Ilyen módon mire tényleg használatba vesszük a Servlet 3.0 API-t, talán már nem is lesz rá szükségünk sokáig. Ugyanakkor az, hogy nem tartunk külön szálat minden kliens kapcsolatnak, még inkáb fontos lesz ha majd egyszer Websocket klienseket szolgálunk ki.
  8. Ezzel kapcsolatban kicsit szorgoskodtam és dolgozok még mindig egy olyan prototipuson, ami a kliens elől eltakarja hogy long-pollt vagy websocket-et használ. Ennek egy működőképes változata itten-e van. Próbáljátok ki: svn co után mvn jetty:run. Ez a prototipus egyébként egy kicsit elvetemültebb koncepció és télleg csak kisérleti jellegű. Ha egyszer megnő, akkor meg fog érdemelni egy magyarázatot itt, addig még kalapálom és dumálunk róla.
Szóval mindez még nagyon messze van az éles bevetéstől.

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. augusztus 28., szombat

Servlet 3.0 első kör

Nagyon érdekelt a servlet 3.0 és közismerten türelmetlen tipus vagyok, letéptem hát a legfrissebb specifikációt és kipróbáltam rajta pár dolgot. A kipróbált implementációk:
  • Jetty 8
  • glassfish 3.0
  • tomcat 7
Hát eléggé fej-fej mellett voltak, úgy tünt kb mind ugyanaddig jutott el. A kisérletsorozatnak nem sikerült kizökkentenie a jetty-pártiságomból. Továbbra is a jetty volt a fejlesztés során a legkönyebben használható. A 8-as verzióhoz is van maven plugin. Pedig próbálgattam a glassfish 3 embedded pluginjét, igazából pozitív meglepetés volt, de hot deployt nem csinált. Szóval minden eltévedésem után újra kellett startolni és packagelni. A tomcat 7.0 az meg maradt a maga kis bemásolom és újraindítom megoldással. Magad uram ha integrációd nincsen.

Servlet annotációk

Iiiigen... aki velem dolgozik, az tudja mennyire nem komállom azt, hogy mindent annotálunk. Az XML HELL sem volt jó, de most elmentünk egy még rosszabb irányba. XML konfigurációkkal még megvolt az esélye annak, hogy áttekinthető marad. Mindenesetre gyors prototype alkalmazásokat az ember biztosan szivesebben tol össze úgy, hogy csak ráannotál a szervletére. Működött is mindenhol.

Azt soha nem értettem egyébként hogy a servlet ikonja az mi akart lenni. Valami ősi dolog lehet a Delphis és Progress-es időkből.

Opcionális búcsú a web.xml-től

Hát igen, ha nincs benne semmi érdekes, akkor le is törölhetjük és a dolog megy nélküle is. Persze ha már egy adatbázis kapcsolat kellene a la JNDI (ami végülis egy hasznos dolog), akkor vissza is jön egyből. Oké, szóval marad, adatbázisa kb mindenkinek van és az operátorok akarják babrálni. (Vajon miért pont azt az egy resource-ot? Az összes többivel miért nem foglalkoznak?)

Asynchron servlet support

Ez talán a legűberebb dolog mióta gyáva programózó vagyok, de az utóbbi 5 évben biztosan a legfontosabb dolog számomra. Nagyon egyszerő, ez történik: amikor úgy véled, hogy sokáig tartó művelet közeledik (pl vársz egy JMS üzenetre) egyszerűen azt mondod, hogy ez a request innentől asszinkron fog kiszolgálódni. Így:
req.startAsync();
Ezután elkérheted a requesthez tartozó AsyncContext ojjektumot

AsyncContext asyncContext = req.getAsyncContext();
Ezt akár bedobhatod egy másik szál által kezelt listába, ahol a lassú műveletre váró összes többi kliens várakozik. Az az egy szál le fogja tudni kezelni minden requestedet. Például valahogy így

asyncContext.getResponse().getWriter().write("gedappa!\n");
Eddig csúcs :) Na akkor most jön a gubanc. Azt is biztosan szeretnénk tudni, hogy mikor kellene ezt a AsyncContext objektumot kidobnunk a listából. Erre tök jó lett volna az AsyncListener osztály. Ami meg is van, kiválló. Példányosul is szépen az implementáció, még mindig oké, de aztán NoSuchMethodError amikor regisztrálni próbáltam, mind a 3 servlet containerben. Ez vagy valami kis lemaradás a specifikációhoz képest, vagy valami szokásos sünös-orákulumos gányolmány.

Elfelejtettem azzal kezdeni, hogy ennek mi értelme van. Talán már kiderült a fentiekből: megszabadulunk az millió kiszolgáló száltól, maradnak csak simán a TCP kapcsolatok. A NIO után végre a következő lépés.

Pár dolgot biztosan érdemes a async kedvéért átgondolni az üzemeltetésben is: például sokan használnak apache httpd-t a servlet container elött, mert példul még php és perl meg egyéb ingyombingyomokat is ezzel a szerverrel hajtanak. A httpd régebben single process per connection cucc volt, most már lehet külön szálakat is használni, de vicces hogy ott álnak majd a szálak, amig a java VM-ben megoldottuk hogy ne kelljen állniuk. Nekem nagyon gyanús, hogy ilyen esetekben sokkal érdemesebb lesz kihozni a servlet containert a httpd mögül. (és persze van, aki azt kérdezi: mit keresett ott addig, ha csak a java elött állt? kiválló kérdés...)

Hát ennyit akartam elmondani már jórég óta, csak mindig belémfolytották a szót. Egyébként komolyan gondolom, hogy ezt a lépést a java világ legnagyobb előrelépésének tartom az utóbbi 4-5 évre. Mondjuk nem kis lépés, de azért szivesen látnék nagyobb lépéseket is.