Innováció a korszerű informatikáért
Vadász Zoltán
Túlterhelés védelem
Gatekeeper funkciók SOA-szolgáltatásbuszokban

Az üzemeltetési elvárásoknál megnéztük, milyen kihívásoknak kell megfelelnie egy szolgáltatásorientált architektúrában (SOA) a köztes rétegnek, azaz a szolgáltatásbusznak. Láthatóvá vált, hogy a modern szolgáltatásbuszok sem rendelkeznek minden követelmény kielégítésére beépített funkcionalitással, így többek között a túlterhelés-védelemhez és erőforrás-menedzsmenthez sem. Akkor a vázolt problémák közül a túlterhelés védelemnél megoldást nyújtó gatekeeper működési elvével és architektúrájának főbb komponenseivel ismerkedtünk meg, mely a piacvezető Oracle Aqualogic Service Bus segítségével valósítja meg az áteresztőképesség-figyelést és szabályozást. Most a GateKeeper megvalósításával foglalkozunk mélyebben.

A következőkben az alkalmazható limitszinteket, timeout-, hiba- és konfigurációkezelést majd az egyes "Stage"-ek és "ErrorHandler"-ek működését, illetve a fontosabb implementációs megoldásokat mutatjuk majd be.

Limitek és konkurens kérések

Limitek és konkurens kérések száma
A GateKeeper három különböző szinten alkalmaz limit értékeket. Az első szint az ESB-projekt, a második az azon belüli szolgáltatás, a harmadik pedig a szolgáltatás hívójának szintje. Az ESB-projekt és a szolgáltatás neve rendelkezésre áll az ESB-konfiguráció előállításakor, viszont a hívórendszer nevét futásidőben kell megszerezni. Ehhez az szükséges, hogy az adott SOA-bevezetésben szabványosításra kerüljön egy egységes üzenetfejléc, melynek többek között része kell legyen a hívórendszer neve is (például a kívánt timeout érték, hívó oldali kérés azonosító stb.), így biztosítva a legalacsonyabb szintű limit alkalmazását is.

Minden egyes limit értékhez a memóriában nyilvántartjuk a pillanatnyi konkurens kérések számát. Egy adott kérés esetében mind a három szinten ellenőrzésre kerül, hogy van-e az adott szinten megadott limit érték, és ha van, az adott limithez számlált konkurens kérések száma eléri-e azt.

Amennyiben a jelen hívással a limitet bármelyik szinten meghaladná a kérések száma, az aktuális kérés végrehajtását megakasztja a GateKeeper.
Ha egyik limit sem sérül, a nyilvántartott konkurens kérések száma minden limitnél eggyel növekszik, és a kérés végrehajtása tovább folytatódik a MessageFlow szerint.
A limitek figyelése a gateKeeperRequest stage-ben történik. A limit túlhaladások itt hibaként jelentkeznek, melyeket a gateKeeperRequest stage ErrorHandlerje kezel oly módon, hogy egy megfelelő – az adott SOA-bevezetésben szabványosított – hibaüzenetet ad válaszként a kérésre. A stage szintű ErrorHandler alkalmazásával biztosítható, hogy az ESB is minimális terhelést kapjon egy elutasított kérés esetén.
A limitek csökkentése a gateKeeperResponse stage-ben és a gateKeeperServiceErrorHandlerben történik.


Timeout és (Service) hibák
Akár időtúllépés akár szolgáltatás szintű hiba lép fel a végrehajtás során, azonosak a GateKeeperrel szembeni elvárások, hiszen mind a két esetben előfordulhat, hogy a hátérrendszerekben továbbra is terhelést okoz a hibás vagy időn túli kérés. A GateKeeper ilyen esetekben ezért nem azonnal csökkenti a nyilvántartott konkurens kérések számát, hanem egy, az adott kéréshez konfigurált késleltetés után. A késleltetés biztosítja, hogy a kérés által, a hátérrendszerekben okozott terhelés lecseng, mire a vonatkozó konkurens kérésszámok csökkennek.
Ha egy kérést a háttérrendszer időtúllépés miatt utasít vissza, az erre vonatkozó információnak az adott SOA-bevezetésben szabványos, előre definiált formában kell megjelennie a válaszban. A GateKeeper így fel tudja ismerni ezeket a válaszokat, és eltérően kezeli a normál válaszoktól.
Az egyes késleltetési értékek a limitekhez hasonlóan három szinten adhatók meg. Az értékek közül mindig a leginkább specifikus vagyis a legbelső szintű kerül alkalmazásra.
Az időtúllépést jelző hibaüzenet vagy hibaazonosító az ESB-projekt és szolgáltatás szintjén kerülhet megadásra. Hasonlóan a késleltetési értékekhez itt is a specifikusabb érték lép érvénybe.
A hibák kezeléséhez szükség van egy ServiceErrorHandler használatára, az időtúllépéseket jelző – az adott SOA bevezetésben szabványos – hátérrendszer válaszok kezelése pedig a normál válaszokkal egyetemben a gateKeeperResponse stage-ben történik.


Konfigurációkezelés
A GateKeeper feladatából fakadóan szükséges, hogy folyamatos üzem mellett is változtatható legyen a konfigurációja. Értelemszerűen újonnan telepített szolgáltatások vagy új ESB-projektek esetén fontos, hogy kiegészíthessük a konfigurációt az azokra vonatkozó limit, késleltetési és időtúllépési értékekkel, illetve a finomhangolási időszakban támogatni kell a konfiguráció dinamikus változtatását is.
Mivel az AquaLogic Service Bus Java „Callout”-ra vonatkozó javaslatok nem ajánlják sem a statikusan sem a dinamikusan indított szálak használatát, a GateKeeper „karbantartási” feladatait is az egyes kérések feldolgozásába kell beépíteni.
A GateKeeper valamennyi kérés kezelése előtt megvizsgálja, hogy újra kell-e tölteni a konfigurációt. A konfigurációs állomány változásának ellenőrzése annak időigényes mivolta miatt nem történik meg minden kéréskor, az azt vezérlő paraméterek (például az előző ellenőrzéstől eltelt idő vagy kérésszám) szintén konfigurálhatók. Tényleges újraolvasás így csak abban az esetben történik, ha a konfiguráció ellenőrzésének ideje a paraméterek szerint eljött, és a konfiguráció valóban változott.
A konfiguráció egy property állományban kerül megadásra, melynek neve gatekeeper.properties és valahol az ESB CLASSPATH-ban kell elhelyezkednie az üzemeltetés által megkívánt helyen.

Egy példa konfigurációs állományra:

Párhuzamosság
A GateKeeper MessageFlow-ból Java Callouttal elérhető metódusát az ESB előre nem megadható mértékben párhuzamosan, konkurensen fogja hívni. Ezért a GateKeeper által kezelt adatok elérése és módosítása szálbiztos (thread-safe) kell legyen.


Konkurens kérések számának kezelése
A szinkronizálásra különös gondot kell fordítani a konkurens kérések számának nyilvántartásakor és módosításakor, ugyanakkor fontos, hogy az alkalmazott szinkronizációk a lehető legkisebb mértékben okozzanak versenyhelyzetet az egyes szálak közt.

A minél alacsonyabb szintű szinkronizáció érdekében a számlálókat külön osztály segítségével tároljuk, módosítjuk és kérdezzük le. Ennek az osztálynak a felelőssége a szinkronizáció és számláló értékének módosítása. Ez a Counter osztály:

A limitekhez nyilvántartott konkurens kérés számlálókat egy „Map”-ben tároljuk, melynek kulcsai a konfigurációs állományban is fellelhető limit nevek lesznek. A fenti konfigurációs állomány esetében a következő kulcsok jöhetnek létre:

A Map szinkronizálása nem szükséges, így a limit-érték ellenőrzések során nem lép fel versenyhelyzet az egyes szálak, csupán a tényleges módosítások közt.
Jogosan merül fel, hogy a limitek garantált betartása érdekében egyetlen szinkronizált kódrészletbe kellene a limitellenőrzéseket és –módosításokat elhelyezni. Az eddigi tapasztalatok viszont azt mutatják, hogy a limitek kismértékű túllépése kisebb üzleti és üzemi kockázatot jelent, mint egy esetleges masszív sorban állás egy-egy magas szintű gyakran ellenőrzött és módosított limit (például igen sok szolgáltatás és nagy hívásszám egyetlen ESB-projekten belül) esetében.


Késleltetések kezelése
A GateKeeper egyik karbantartandó adatstruktúrája a késleltetett konkurens kérésszám csökkentéseket tartalmazó Map. A konfiguráció újratöltéséhez hasonlóan a késleltetések feldolgozása nem végezhető el külön szálban, így a normál üzem során hívott funkciókhoz kapcsolódóan történik ezeknek az adatoknak a karbantartása. Másrészről biztosítani kell, hogy a késleltetések mentése és feldolgozása a párhuzamos hívások során is helyesen valósuljon meg.
A késleltetéseket egy rendezett Map tárolja, melyben a kulcsok a késleltetések lejárati időpontjai, a tárolt értékek pedig azon kérés paraméterei, amelyekre a vonatkozó konkurens kérés számlálókat az adott időpontot követően csökkenteni kell. Tény, hogy késleltetés hozzáadásával vagy feldolgozásával nagyságrendekkel ritkábban kell foglalkozni, mint limitmódosítással, így a sebességre vonatkozó követelmények ebben az esetben nem olyan szigorúak, mint a limitmódosítások esetében. A késleltetett konkurens számlálócsökkentések hozzáadásának helyei már ismertek, feldolgozásuk pedig akkor történik meg:

amennyiben egy limitellenőrzéskor limitmeghaladás történik
mielőtt új késleltetett konkurensszámláló csökkentés kerül felvételre.
Limitmeghaladás esetén, a feldolgozás után a limiteket ismét ellenőrzik, hátha a késleltetett csökkentések feldolgozása során a számláló a limit alá csökkent.
A késleltetett csökkentéseket feldolgozó sematikus kódrészlet:

gateKeeperRequest Stage
A PipelinePair request ágába a már korábban meglévő AuditLogger komponens logRequest stage-e elé, vagyis a flow első lépéseként kerül be a GateKeeper első eleme, a gateKeeperRequest stage. A gateKeeperRequest stage mögött egy Java Callout akció áll, ami a gatekeeper.jar-ban található hu.mt.gatekeeper.GateKeeper.gateKeeperRequest() metódust hívja meg, a funkcionalitáshoz szükséges paraméterekkel:

  • kérésazonosító (főképp naplózási és monitoring célokkal),
  • ESB-projekt neve,
  • szolgáltatás neve,
  • hívórendszer neve.

gateKeeperRequest stage

A gateKeeperRequest metódus regisztrálja, számolja a beérkező kérést, feldolgozza a paramétereket, és a konfigurációban beállított adott ESB-projekt, szolgáltatás, valamint hívórendszer terhelhetőségi limitét figyelembe véve átengedi vagy elutasítja a kérést. Amennyiben a konfigurációban egyik szinten sincs megadva limitérték, a kérést átengedi.
Mivel a limitek lekérdezése a GateKeeper által leggyakrabban végrehajtott funkció, fontos, hogy gyors és egyszerű legyen.
Elutasítás esetén egy RequestRejectedException jön létre, amit a gateKeeperRequestErrorHandler metódus kezel.


gateKeeperRequestErrorHandler stage
Ez a stage az egyes limitek védelmében a gateKeeperRequest által kiváltott hibákat kezeli. Abban az esetben, amikor a kérést a gateKeeperRequestStage elutasítja, a kérésnek, azaz a szolgáltatásnak megfelelő válasz-xml állítódik össze, mely csak egy – az adott SOA-bevezetésben szabványos – ServiceFault objektumot tartalmaz. Az objektum leírja az elutasítás okát is, vagyis a túlterheltséget.

A válasz összeállítása a ”stage”-ben folyik, hogy könnyen igazítható legyen az adott SOA-bevezetés igényeihez. A MessageFlow-nak ez a szakasza természetesen szintén generálható, így a környezeti igényekhez igazítást nem szolgáltatásonként, hanem az ESB-konfiguráció generátorban kell elvégezni.
Ha az adott kérést a gateKeeperRequest stage visszautasítja, akkor annak XML-jét a GateKeeper naplófájlban naplózzák, az elutasítást kiváltó limitszinttel és -értékkel együtt. Így azonosíthatók, monitorozhatók, elemezhetők és szükség esetén könyebben újrafuttathatók a visszautasított kérések.

gateKeeperRequestErrorHandler stage

gateKeeperResponse stage
A response ágba a már korábban meglévő AuditLogger komponens logRequest stage-e után, vagyis a flow utolsó lépéseként kerül a gateKeeperResponse stage. A megvalósítás szintén Java Callout akcióval történik, melynek során a hu.mt.gatekeeper.GateKeeper.gateKeeperResponse() metódust kerül meghívásra, a funkcionalitáshoz szükséges paraméterekkel:

  • kérésazonosító (főképp naplózási és monitoring célokkal),
  • ESB-projekt neve,
  • szolgáltatás neve,
  • hívórendszer neve,
  • szabványos hibaazonosító,
  • szabványos hibaüzenet.

A gateKeeperResponse metódus regisztrálja a válaszokat, feldolgozza a paramétereket, és az adott ESB-projekt, -szolgáltatás és hívórendszer konkurens kérések darabszámát csökkenti eggyel. Ha az adott kérésre a háttérrendszer a korábban leírt módon időtúllépést jelentő választ ad, akkor a konkurens kérések számát nem azonnal csökkenti, hanem az adott kérés paramétereivel egy késleltetett konkurens kérésszámláló csökkentést regisztrál a rendszer.

gateKeeperResponse stage

gateKeeperServiceErrorHandler
Amennyiben a végrehajtás során valamilyen ServiceFault keletkezik, azt a ServiceErrorHandler kezeli. A gateKeeperServiceErrorHandler stage mögött egyetlen Java Callout akció áll, ami a gatekeeper.jar-ban található hu.mt.gatekeeper.GateKeeper.gateKeeperServiceErrorhandler() metódust hívja meg a funkcionalitáshoz szükséges paraméterekkel:

  • kérés azonosító (főképp naplózási és monitoring célokkal),
  • ESB-projekt neve,
  • szolgáltatás neve,
  • hívórendszer neve.

ServiceFault esetén, a timeout esetével analóg módon a GateKeeper csak a megadott késleltetés eltelte után fogja csökkenteni a konkurens hívások számát. Természetesen a „ServiceFault”-ot érintetlenül továbbterjeszti.

gateKeeperServiceErrorHandler stage

Memóriaigény
A GateKeeper tervezése során különös gondot kellett fordítani annak, a lehetőségek szerinti alacsony memóriahasználatára, hiszen az ESB-szerverekben az XML-alapú üzenetek feldolgozása (parse) és esetenkénti transzformálása eleve nagy memóriaigényű folyamat.

A GateKeeper a következő adatokat tárolja a memóriában:
Statikusan:

  • Konfigurációs adatok, úgymint:
  • Limitértékek,
  • Késleltetés értékeke,
  • Timeout hibaüzenetek és/vagy hibakódok,
  • Egyéb konfigurációs adatok: konfiguráció újratöltés etc.
  • Konkurens kérésszámlálók: minden, a kérések által már érintett limithez
  • Késleltetett konkurens kérésszámláló csökkentések.

Minden egyes kérésre a MessageFlow-ban definiált változókban dinamikusan:

  • requestID: String,
  • requestSenderID: String.

Látható, hogy a GateKeeper dinamikus, azaz a kérésektől függő memóriahasználata igen csekély, mindössze két String, néhány kilobyte, és lineárisan viszonyul a kérések számához. A statikusan igénybe vett memória mérete pedig – szintén lineárisan – a konfiguráció méretétől függ.
A GateKeeper maximális memóriahasználatát – azonos terhelés és azonos konfiguráció esetén – akkor éri el, amikor a konfiguráció változása miatt az éppen betöltés alatt áll. Amíg az új konfiguráció életbe nem lép mind az elavult, mind az új konfigurációs adatok megtalálhatók a memóriában.
Összességében kijelenthető, hogy a GateKeeper memóriahasználata még egy komolyan terhelt és kiterjedt rendszer esetében sem haladja meg az 5-10MB-ot, ami igen kedvező érték.
Processzorhasználat, GateKeeper átfutási idő kérésenként
Annak érdekében, hogy a GateKeeper az egyes üzenetek feldolgozásában okozott késleltetése pontosan mérhető és vizsgálható legyen azt a tervezés és a fejlesztés során monitorozhatóvá kellett tenni. A monitorozás célja, hogy a GateKeeper által okozott átlagos késleltetések akár egy éles rendszeren is mérhetőek és könnyen lekérdezhetőek legyenek.
Az egyes beépített mérési pontok információkat szolgáltatnak arról, hogy a GateKeeper Java Callout-jaiban az egyes munkaütemek átlagosan mennyi ideig futottak, azaz mekkora átlagos késleltetést, ezáltal processzor terhelést okoz például egy limitkeresés vagyis a késleltetett csökkentések feldolgozása. Az így beépített monitorozási pontok segítségével gazdag tapasztalatokra lehet szert tenni bármely éles üzemi környezetről is.
Az alábbi kivonatos táblázatban látható egy-egy monitorozott szakasz, illetve az annak végrehajtásához szükséges átlagos idő, milisecundumban.

Label Avg
GateKeeper_Self_Monitoring.Limit.getValueFor(), ms. 2
GateKeeper_Self_Monitoring.REQUEST_ERROR_PROCESSING, ms. 2
GateKeeper_Self_Monitoring.REQUEST_PROCESSING, ms. 10
GateKeeper_Self_Monitoring.REQUEST_PROCESSING.2.decideBasedOnLimit, ms. 3
GateKeeper_Self_Monitoring.RESPONSE_PROCESSING, ms 4
GateKeeper_Self_Monitoring.SERVICE_ERROR_PROCESSING, ms. 6
GateKeeper_Self_Monitoring.checkAndLoadProperties.processTime, ms. 140
GateKeeper_Self_Monitoring.placeDelayedDecrease, ms. 0
GateKeeper_Self_Monitoring.processDelayedDecreases, ms. 4


A fenti táblázatból is kitűnik, hogy például egy limitlekérdezés átlagosan 2 ms-ot vesz igénybe, annak eldöntése, hogy a kérés átengedhető-e 3 ms-ot. Egy sikeres választ produkáló és a GateKeeper által átengedett kérés összesen 14 ms késleltetést szenved, egy, a hátérrendszerben valamilyen hibára futott kérés pedig 16 ms-ot. A késleltetések feldolgozása ebben a rendszerben átlagosan 4ms volt, míg a konfiguráció betöltése majd érvénybe léptetése pedig 140 ms.

Összegzés
A GateKeeper egy hatékony és erőforrás-takarékos megoldás a SOA-architektúrák egy eleddig nyitott problémájára az áteresztőképesség figyelésére és szabályozására. Kijelenthető, hogy alkalmazása során az Aqualogic Service Bus futtató környezetével szemben támasztott többlet erőforrásigénye minimális, emellett pedig egy rugalmasan alkalmazható eszköz az üzemeltetés kezében a rendszerek túlterhelés elleni védelmében. Folyamatban van a GateKeeper továbbfejlesztése a kliens csatornánként engedélyezett sávszélesség (tranzakció/időegység) figyelése és szabályozása érdekében, a tervezéskor és implementáláskor figyelembe vettük az eddig összegyűlt tapasztalatokat, statisztikákat.