Programovanie

Zhromažďujte zdroje pomocou Apache Commons Pool Framework

Združovanie zdrojov (tiež nazývané združovanie objektov) medzi viacerými klientmi je technika používaná na podporu opätovného použitia objektov a na zníženie režijných nákladov pri vytváraní nových zdrojov, čo vedie k lepšiemu výkonu a priepustnosti. Predstavte si náročnú serverovú aplikáciu Java, ktorá odosiela stovky dotazov SQL otváraním a zatváraním pripojení pre každú požiadavku SQL. Alebo webový server, ktorý slúži stovkám požiadaviek HTTP a každú požiadavku vybavuje vytvorením samostatného vlákna. Alebo si predstavte vytvorenie inštancie analyzátora XML pre každú požiadavku na analýzu dokumentu bez opätovného použitia inštancií. Toto sú niektoré zo scenárov, ktoré si vyžadujú optimalizáciu použitých zdrojov.

Využitie zdrojov sa môže občas ukázať ako rozhodujúce pre náročné aplikácie. Niektoré slávne webové stránky boli vypnuté pre svoju neschopnosť zvládnuť ťažké bremená. Väčšinu problémov spojených s ťažkými bremenami je možné vyriešiť na makroúrovni pomocou funkcií zoskupovania a vyrovnávania záťaže. Na aplikačnej úrovni zostávajú obavy týkajúce sa nadmerného vytvárania objektov a dostupnosti obmedzených serverových zdrojov, ako sú pamäť, procesor, vlákna a databázové pripojenia, ktoré by mohli predstavovať potenciálne prekážky a ak nebudú optimálne využité, zničiť celý server.

V niektorých situáciách môže politika používania databázy vynútiť obmedzenie počtu súbežných pripojení. Externá aplikácia tiež môže diktovať alebo obmedzovať počet súbežných otvorených pripojení. Typickým príkladom je doménový register (napríklad Verisign), ktorý obmedzuje počet dostupných aktívnych pripojení soketu pre registrátorov (napríklad BulkRegister). Združovanie zdrojov sa ukázalo ako jedna z najlepších možností pri riešení týchto typov problémov a do istej miery pomáha aj pri udržiavaní požadovaných úrovní služieb pre podnikové aplikácie.

Väčšina dodávateľov aplikačných serverov J2EE poskytuje združovanie zdrojov ako neoddeliteľnú súčasť svojich kontajnerov Web a EJB (Enterprise JavaBean). V prípade databázových pripojení dodávateľ servera zvyčajne poskytuje implementáciu servera Dátový zdroj rozhranie, ktoré pracuje v spojení s dodávateľom ovládačov JDBC (Java Database Connectivity) ConnectionPoolDataSource implementácia. The ConnectionPoolDataSource implementácia slúži ako továreň na pripojenie správcu zdrojov pre združené java.sql.Connection predmety. Podobne sú inštancie EJB bezstavových bôbov relácie, bôbov riadených správami a bánk entít združené v kontajneroch EJB kvôli vyššej priepustnosti a výkonu. Inštancie analyzátora XML sú tiež kandidátmi na združovanie, pretože vytváranie inštancií syntaktického analyzátora spotrebuje veľkú časť zdrojov systému.

Úspešnou implementáciou spoločného využívania zdrojov s otvoreným zdrojom je DBCP rámca Commons Pool, komponent združujúci databázové pripojenie od Apace Software Foundation, ktorý sa vo veľkej miere používa v podnikových aplikáciách produkčnej triedy. V tomto článku stručne diskutujem o vnútorných aspektoch rámca Commons Pool a potom ho použijem na implementáciu fondu vlákien.

Najprv sa pozrime na to, čo tento rámec poskytuje.

Rámec spoločného fondu

Rámec Commons Pool ponúka základnú a robustnú implementáciu na združovanie ľubovoľných objektov. K dispozícii je niekoľko implementácií, ale pre účely tohto článku používame najobecnejšiu implementáciu, GenericObjectPool. Používa a CursorableLinkedList, čo je implementácia dvojnásobne prepojeného zoznamu (súčasť zbierok Jakarta Commons) ako podkladová dátová štruktúra na uchovávanie združovaných objektov.

Rámec navyše poskytuje množinu rozhraní, ktoré poskytujú metódy životného cyklu a pomocné metódy na správu, monitorovanie a rozširovanie fondu.

Rozhranie org.apache.commons.PoolableObjectFactory definuje nasledujúce metódy životného cyklu, ktoré sa ukážu ako nevyhnutné pre implementáciu zložky združovania:

 // Vytvorí inštanciu, ktorá môže byť vrátená verejným bazénom objektu makeObject () {} // Zničí inštanciu, ktorú už bazén nepotrebuje, verejné void destruObject (Object obj) {} // Pred použitím objektu typu public boolean validateObject overte objekt (Object obj) {} // Inicializácia inštancie, ktorá sa má vrátiť verejným voidom fondu ActivObject (Object obj) {} // Inicializácia inštancie, ktorá sa má vrátiť do verejného public void passivateObject (Object obj) {}

Ako môžete zistiť podľa podpisov metód, toto rozhranie sa primárne zaoberá týmto:

  • makeObject (): Implementácia vytvorenia objektu
  • destruObject (): Implementujte zničenie objektu
  • validateObject (): Pred použitím objekt overte
  • activObject (): Implementujte inicializačný kód objektu
  • passivateObject (): Implementujte kód na neinicializáciu objektu

Ďalšie základné rozhranie -org.apache.commons.ObjectPool—Definuje nasledujúce metódy riadenia a monitorovania fondu:

 // Získať inštanciu z môjho fondu Object loanObject () throws Exception; // Vráti inštanciu do môjho fondu void returnObject (Object obj) hodí Exception; // Invalidates an object from the pool void invalidateObject (Object obj) throws Exception; // Používa sa na predbežné načítanie fondu s nečinnými objektmi void addObject () throws Exception; // Vráti počet nečinných inštancií int getNumIdle () hodí UnsupportedOperationException; // Vráti počet aktívnych inštancií int getNumActive () hodí UnsupportedOperationException; // Vymaže nečinné objekty void clear () hodí Exception, UnsupportedOperationException; // Close the pool void close () throws Exception; // Nastaví ObjectFactory na použitie na vytváranie inštancií void setFactory (továreň PoolableObjectFactory) hodí IllegalStateException, UnsupportedOperationException;

The ObjectPool implementácia rozhrania vyžaduje a PoolableObjectFactory ako argument vo svojich konštruktoroch, čím deleguje vytváranie objektov na svoje podtriedy. Nehovorím tu veľa o dizajnových vzoroch, pretože to nie je naším zameraním. Pre čitateľov, ktorí sa chcú pozrieť na diagramy tried UML, si pozrite zdroje.

Ako už bolo spomenuté vyššie, trieda org.apache.commons.GenericObjectPool je iba jednou implementáciou org.apache.commons.ObjectPool rozhranie. Rámec tiež poskytuje implementácie pre fondy kľúčových objektov pomocou rozhraní org.apache.commons.KeyedObjectPoolFactory a org.apache.commons.KeyedObjectPool, kde je možné združiť skupinu s kľúčom (ako v HashMap), a teda spravovať viac združení.

Kľúč k úspešnej stratégii združovania závisí od toho, ako nakonfigurujeme fond. Zle nakonfigurované fondy môžu byť zdrojové kódy, ak konfiguračné parametre nie sú dobre vyladené. Pozrime sa na niektoré dôležité parametre a ich účel.

Podrobnosti o konfigurácii

Bazén je možné konfigurovať pomocou GenericObjectPool.Config triedy, čo je statická vnútorná trieda. Prípadne môžeme použiť GenericObjectPoolnastavovacie metódy na nastavenie hodnôt.

Nasledujúci zoznam podrobne uvádza niektoré z dostupných konfiguračných parametrov pre server GenericObjectPool implementácia:

  • maxIdle: Maximálny počet inštancií spánku v bazéne bez uvoľnenia ďalších objektov.
  • min: Minimálny počet inštancií spánku v bazéne bez vytvárania ďalších objektov.
  • maxActive: Maximálny počet aktívnych inštancií vo fonde.
  • timeB BetweenEvictionRunsMillis: Počet milisekúnd, ktoré sa majú uspať medzi spusteniami vlákna vylúčenia nečinných objektov. Ak je záporné, nebude sa spúšťať vlákno vypudzovania nečinných objektov. Tento parameter použite, iba ak chcete spustiť vlákno evictor.
  • minEvictableIdleTimeMillis: Minimálna doba, počas ktorej môže objekt, ak je aktívny, nečinne sedieť v bazéne, kým sa ho nepodarí vylúčiť pomocou vyhadzovača nečinných objektov. Ak zadáte zápornú hodnotu, nevypudia sa žiadne objekty iba kvôli nečinnosti.
  • testOnBorrow: Keď je hodnota „true“, objekty sa overia. Ak sa nepodarí overiť objekt, bude vyhodený z fondu a fond sa pokúsi požičať ďalší.

Pre vyššie uvedené parametre by mali byť poskytnuté optimálne hodnoty, aby sa dosiahol maximálny výkon a výkon. Pretože sa vzor použitia líši od aplikácie k aplikácii, vyladite fond rôznymi kombináciami parametrov, aby ste dosiahli optimálne riešenie.

Aby sme pochopili viac o fonde a jeho vnútorných častiach, poďme implementovať fond vlákien.

Navrhované požiadavky na skupinu vlákien

Predpokladajme, že nám bolo povedané, aby sme navrhli a implementovali komponentu fondu vlákien pre plánovač úloh, aby spúšťali úlohy v zadaných harmonogramoch a hlásili dokončenie a prípadne výsledok vykonania. V takomto scenári je cieľom nášho fondu vlákien zhromaždiť nevyhnutný počet vlákien a vykonať naplánované úlohy v nezávislých vláknach. Požiadavky sú zhrnuté takto:

  • Vlákno by malo byť schopné vyvolať ľubovoľnú metódu ľubovoľnej triedy (naplánovanú úlohu)
  • Vlákno by malo byť schopné vrátiť výsledok vykonania
  • Vlákno by malo byť schopné hlásiť dokončenie úlohy

Prvá požiadavka poskytuje priestor pre voľne spojenú implementáciu, pretože nás nenúti implementovať podobné rozhranie Spustiteľné. Tiež uľahčuje integráciu. Našu prvú požiadavku môžeme implementovať poskytnutím vlákna týmto informáciám:

  • Názov triedy
  • Názov metódy, ktorá sa má vyvolať
  • Parametre, ktoré sa majú odovzdať metóde
  • Typy parametrov odovzdaných parametrov

Druhá požiadavka umožňuje klientovi používajúcemu vlákno na získanie výsledku vykonania. Jednoduchou implementáciou by bolo uložiť výsledok vykonania a poskytnúť prístupovú metódu ako getResult ().

Tretia požiadavka do istej miery súvisí s druhou požiadavkou. Hlásenie o dokončení úlohy môže tiež znamenať, že klient čaká na výsledok vykonania. Aby sme túto schopnosť zvládli, môžeme poskytnúť určitú formu mechanizmu spätného volania. Najjednoduchší mechanizmus spätného volania je možné implementovať pomocou java.lang.Objektje počkaj () a upozorniť () sémantika. Prípadne môžeme použiť Pozorovateľ vzor, ​​ale zatiaľ si veci zjednodušme. Mohlo by vás lákať použiť java.lang.Thread triedy pripojiť sa () metóda, ale to nebude fungovať, pretože združené vlákno nikdy nedokončí svoje run () metóda a beží ďalej, kým to fond potrebuje.

Teraz, keď máme pripravené naše požiadavky a približnú predstavu o tom, ako implementovať fond vlákien, je čas urobiť nejaké skutočné kódovanie.

V tejto fáze vyzerá náš diagram triedy UML navrhovaného dizajnu ako na obrázku nižšie.

Implementácia fondu vlákien

Objekt vlákna, ktorý sa chystáme združiť, je vlastne obal okolo objektu vlákna. Zavolajme obal WorkerThread triedy, ktorá rozširuje java.lang.Thread trieda. Skôr ako začneme programovať WorkerThread, musíme implementovať rámcové požiadavky. Ako sme už videli skôr, musíme implementovať PoolableObjectFactory, ktorý funguje ako továreň, aby vytvoril našu poolable WorkerThreads. Keď je továreň pripravená, implementujeme ThreadPool rozšírením GenericObjectPool. Potom skončíme WorkerThread.

Implementácia rozhrania PoolableObjectFactory

Začíname s PoolableObjectFactory rozhranie a pokúsiť sa implementovať potrebné metódy životného cyklu pre náš fond vlákien. Píšeme továrenskú triedu ThreadObjectFactory nasledovne:

verejná trieda ThreadObjectFactory implementuje PoolableObjectFactory {

public Object makeObject () {return new WorkerThread (); } public void destruObject (Object obj) {if (obj instanceof WorkerThread) {WorkerThread rt = (WorkerThread) obj; rt.setStopped (true); // Zastavenie bežiaceho vlákna}} public boolean validateObject (Object obj) {if (obj instanceof WorkerThread) {WorkerThread rt = (WorkerThread) obj; if (rt.isRunning ()) {if (rt.getThreadGroup () == null) {return false; } návrat pravdivý; }} návrat true; } public void activObject (objekt obj) {log.debug ("activObject ..."); }

public void passivateObject (Object obj) {log.debug ("passivateObject ..." + obj); if (obj instanceof WorkerThread) {WorkerThread wt = (WorkerThread) obj; wt.setResult (null); // Vyčistenie výsledku vykonania}}}

Prejdime si podrobne každú metódu:

Metóda makeObject () vytvára WorkerThread objekt. Pri každej požiadavke sa kontroluje fond, aby sa zistilo, či sa má vytvoriť nový objekt alebo či sa má znovu použiť existujúci objekt. Napríklad, ak je konkrétna požiadavka prvou požiadavkou a fond je prázdny, znak ObjectPool výzvy na implementáciu makeObject () a pridáva WorkerThread k bazénu.

Metóda destruObject () odstráni WorkerThread objekt z fondu nastavením booleovskej vlajky a tým zastavením bežiaceho vlákna. Na tento kúsok sa pozrieme neskôr, ale všimnite si, že teraz preberáme kontrolu nad tým, ako sa ničia naše objekty.

$config[zx-auto] not found$config[zx-overlay] not found