Programiranje

Združite vire z uporabo Apache's Commons Pool Framework

Združevanje virov (imenovano tudi združevanje predmetov) med več odjemalci je tehnika, ki se uporablja za spodbujanje ponovne uporabe predmetov in zmanjšanje stroškov za ustvarjanje novih virov, kar ima za posledico boljšo zmogljivost in pretočnost. Zamislite si močno strežniško aplikacijo Java, ki pošlje na stotine poizvedb SQL z odpiranjem in zapiranjem povezav za vsako zahtevo SQL. Ali pa spletni strežnik, ki streže na stotine zahtev HTTP, pri čemer vsako zahtevo obdela tako, da ustvari ločeno nit. Ali pa si predstavljajte, da ustvarite primerek razčlenjevalnika XML za vsako zahtevo za razčlenitev dokumenta brez ponovne uporabe primerkov. To je nekaj scenarijev, ki upravičujejo optimizacijo uporabljenih virov.

Uporaba virov se lahko včasih izkaže za kritično za težke aplikacije. Nekatera znana spletna mesta so se zaprla zaradi njihove nezmožnosti prenašanja velikih obremenitev. Večino težav, povezanih s težkimi obremenitvami, je mogoče rešiti na makro ravni z uporabo zmogljivosti združevanja v skupine in uravnavanja obremenitve. Zaskrbljenost ostaja na ravni aplikacije v zvezi s čezmernim ustvarjanjem predmetov in razpoložljivostjo omejenih strežniških virov, kot so pomnilnik, CPU, niti in povezave z bazami podatkov, kar bi lahko predstavljalo potencialna ozka grla in, če se ne uporabi optimalno, porušilo celoten strežnik.

V nekaterih primerih lahko politika uporabe zbirke podatkov uveljavi omejitev števila sočasnih povezav. Poleg tega lahko zunanja aplikacija narekuje ali omejuje število sočasnih odprtih povezav. Tipičen primer je register domen (na primer Verisign), ki omejuje število razpoložljivih povezav aktivnih vtičnic za registratorje (na primer BulkRegister). Združevanje virov se je izkazalo za eno najboljših možnosti pri reševanju tovrstnih težav in v določeni meri pomaga tudi pri vzdrževanju zahtevanih ravni storitev za poslovne aplikacije.

Večina ponudnikov aplikacijskih strežnikov J2EE zagotavlja združevanje virov kot sestavni del svojih vsebnikov Web in EJB (Enterprise JavaBean). Za povezave z bazo podatkov prodajalec strežnika običajno zagotovi izvedbo Vir podatkov vmesnik, ki deluje v povezavi s ponudnikom gonilnikov JDBC (Java Database Connectivity) ConnectionPoolDataSource izvajanje. The ConnectionPoolDataSource izvedba služi kot tovarna povezav upravitelja virov za združeno java.sql.Connection predmetov. Podobno so primeri EJB fižol seje brez stanja, fižol na osnovi sporočil in fižol entitet združeni v vsebnike EJB za večjo pretočnost in zmogljivost. Primerki razčlenjevalnika XML so prav tako kandidati za združevanje, ker ustvarjanje primerkov razčlenjevalca porabi veliko sistemskih virov.

Uspešna izvedba odprtokodnega združevanja virov je DBCP ogrodja Commons Pool, komponenta združevanja baz podatkov povezave Apace Software Foundation, ki se pogosto uporablja v poslovnih aplikacijah proizvodnega razreda. V tem članku na kratko razpravljam o notranjih delih okvira Commons Pool in ga nato uporabim za izvedbo področja niti.

Poglejmo najprej, kaj zagotavlja ogrodje.

Commons Pool framework

Okvir Commons Pool ponuja osnovno in trdno izvedbo za združevanje poljubnih predmetov. Na voljo je več izvedb, vendar za namene tega članka uporabljamo najbolj generično izvedbo, GenericObjectPool. Uporablja a CursorableLinkedList, ki je dvojno povezana izvedba seznama (del zbirke zbirk v Džakarti), kot temeljna podatkovna struktura za hrambo predmetov, ki so združeni.

Poleg tega ogrodje ponuja nabor vmesnikov, ki zagotavljajo metode življenjskega cikla in pomožne metode za upravljanje, spremljanje in razširitev bazena.

Vmesnik org.apache.commons.PoolableObjectFactory opredeljuje naslednje metode življenjskega cikla, ki se izkažejo za bistvene za izvajanje komponente združevanja:

 // Ustvari primerek, ki ga lahko vrne javni objekt bazena Object makeObject () {} // Uniči primerek, ki ni več potreben v javni void skupine poolObject (Object obj) {} // Potrdite objekt, preden ga uporabite javno logično validateObject (Obj. Obj) {} // Inicializirajte primerek, ki ga mora vrniti javna praznina bazena activateObject (Object obj) {} // Neinicializirajte primerek, ki se vrne v javno void pasivateObject baze (Objekt obj) {}

Kot lahko razberete s podpisi metode, ta vmesnik obravnava predvsem naslednje:

  • makeObject (): Izvedite ustvarjanje predmeta
  • killObject (): Izvedite uničenje predmeta
  • validateObject (): Pred uporabo preverite veljavnost predmeta
  • activateObject (): Izvedite kodo za inicializacijo objekta
  • passivateObject (): Implementirajte kodo za neinicializacijo predmeta

Še en osrednji vmesnik -org.apache.commons.ObjectPool—Opredeljuje naslednje metode za upravljanje in spremljanje bazena:

 // Pridobim primerek iz mojega bazena. Object borrowObject () vrže izjemo; // Vrni primerek v moj bazen void returnObject (Object obj) vrže izjemo; // razveljavi objekt iz bazena void invalidateObject (Object obj) vrže izjemo; // Uporablja se za predhodno nalaganje bazena z nedejavnimi predmeti void addObject () vrže izjemo; // Vrne število nedejavnih primerkov int getNumIdle () vrže UnsupportedOperationException; // Vrne število aktivnih primerkov int getNumActive () vrže UnsupportedOperationException; // počisti neaktivne predmete void clear () vrže Exception, UnsupportedOperationException; // Zapiranje bazena void close () vrže Exception; // Nastavimo ObjectFactory za uporabo pri ustvarjanju primerov void setFactory (PoolableObjectFactory factory) vrže IllegalStateException, UnsupportedOperationException;

The ObjectPool izvedba vmesnika traja PoolableObjectFactory kot argument v svojih konstruktorjih, s čimer se ustvarjanje predmeta prenese na njegove podrazrede. Tu ne govorim veliko o vzorcih oblikovanja, saj to ni naša pozornost. Za bralce, ki jih zanima ogled diagramov razredov UML, glejte Viri.

Kot že omenjeno, razred org.apache.commons.GenericObjectPool je le ena izvedba org.apache.commons.ObjectPool vmesnik. Okvir ponuja tudi izvedbe za področja objektov s ključi z uporabo vmesnikov org.apache.commons.KeyedObjectPoolFactory in org.apache.commons.KeyedObjectPool, kjer lahko združimo bazen s ključem (kot v HashMap) in tako upravlja več bazenov.

Ključ do uspešne strategije združevanja je odvisen od tega, kako konfiguriramo področje. Če konfiguracijski parametri niso dobro nastavljeni, so lahko slabo konfigurirana področja resnična prašiča. Oglejmo si nekaj pomembnih parametrov in njihov namen.

Podrobnosti o konfiguraciji

Področje lahko konfigurirate s pomočjo GenericObjectPool.Config razred, ki je statični notranji razred. Lahko pa uporabimo le GenericObjectPoolnastavitvene metode za nastavitev vrednosti.

Naslednji seznam podrobno opisuje nekatere razpoložljive konfiguracijske parametre za GenericObjectPool izvedba:

  • maxIdle: Največje število primerov spanja v bazenu, brez sprostitve dodatnih predmetov.
  • minIdle: Najmanjše število primerov spanja v bazenu, brez ustvarjanja dodatnih predmetov.
  • maxActive: Največje število aktivnih primerkov v področju.
  • timeBetweenEvictionRunsMillis: Število milisekund mirovanja med zagonom niti izselitvenega predmeta v prostem teku. Če je negativno, se ne bo izvajala nit izselitvenega predmeta v prostem teku. Ta parameter uporabite samo, če želite, da se nit izselitve izvaja.
  • minEvictableIdleTimeMillis: Najmanjši čas, v katerem lahko objekt, če je aktiven, sedi v prostem teku v bazenu, preden je primeren za izselitev s strani izselitvenega objekta v prostem teku. Če je navedena negativna vrednost, se noben predmet ne preseli samo zaradi prostega teka.
  • testOnButra: Ko je "true", se predmeti preverijo. Če objekt ne uspe preveriti veljavnosti, bo izpuščen iz bazena in bazen bo poskušal izposoditi drugega.

Za zgornje parametre je treba zagotoviti optimalne vrednosti, da se doseže največja zmogljivost in prepustnost. Ker se vzorec uporabe razlikuje od aplikacije do aplikacije, prilagodite področje z različnimi kombinacijami parametrov, da boste dobili optimalno rešitev.

Da bi razumeli več o področju in njegovih notranjih delih, uvedimo področje niti.

Predlagane zahteve za področje niti

Recimo, da smo bili naročeni, da načrtujemo in implementiramo komponento nabora niti za načrtovalnik opravil, da sproži opravila po določenih urnikih in poroča o zaključku in, morda, o rezultatu izvedbe. V takem scenariju je cilj našega področja niti združiti predpogojno število niti in izvesti načrtovana opravila v neodvisnih nitih. Zahteve so povzete na naslednji način:

  • Nit mora biti sposoben priklicati poljubno metodo razreda (načrtovano opravilo)
  • Nit mora biti sposoben vrniti rezultat izvedbe
  • Nit bi moral biti sposoben poročati o zaključku naloge

Prva zahteva daje prostor za ohlapno povezano izvedbo, saj nas ne sili k izvajanju vmesnika, kot je Teče. Olajša tudi integracijo. Svojo prvo zahtevo lahko uresničimo tako, da nitimo naslednje informacije:

  • Ime predavanja
  • Ime metode, ki jo je treba priklicati
  • Parametri, ki se posredujejo metodi
  • Vrste parametrov posredovanih parametrov

Druga zahteva omogoča, da odjemalec, ki uporablja nit, prejme rezultat izvedbe. Preprosta izvedba bi bila shranjevanje rezultata izvedbe in zagotovitev metode dostopa, kot je getResult ().

Tretja zahteva je nekoliko povezana z drugo zahtevo. Poročanje o zaključku naloge lahko pomeni tudi, da odjemalec čaka, da dobi rezultat izvedbe. Za obvladovanje te zmožnosti lahko zagotovimo neko obliko mehanizma povratnega klica. Najenostavnejši mehanizem povratnega klica lahko izvedemo z uporabo java.lang.Objectje počakaj () in obvestiti() semantika. Lahko pa uporabimo tudi Opazovalec vzorec, za zdaj pa naj bodo stvari preproste. Morda vas bo zamikalo, da bi uporabili java.lang.Nit razredov pridruži se () metoda, vendar to ne bo delovalo, saj združena nit nikoli ne dokonča svoje teči () metoda in deluje, dokler bazen potrebuje.

Zdaj, ko imamo pripravljene zahteve in grobo idejo, kako uporabiti področje niti, je čas, da naredimo nekaj resničnega kodiranja.

Na tej stopnji je naš diagram razreda UML predlagane zasnove videti kot spodnja slika.

Izvajanje področja niti

Predmet niti, ki ga bomo združili, je pravzaprav ovoj okoli predmeta niti. Pokličimo ovitek z WorkerThread razred, ki razširja java.lang.Nit razred. Preden začnemo kodirati WorkerThread, moramo izvajati okvirne zahteve. Kot smo že videli, moramo implementirati PoolableObjectFactory, ki deluje kot tovarna, da ustvari našo združljivost WorkerThreads. Ko je tovarna pripravljena, izvedemo ThreadPool s podaljšanjem GenericObjectPool. Nato zaključimo svoje WorkerThread.

Izvajanje vmesnika PoolableObjectFactory

Začnemo z PoolableObjectFactory vmesnik in poskusite izvesti potrebne metode življenjskega cikla za naše področje niti. Pišemo tovarniški razred ThreadObjectFactory kot sledi:

javni razred ThreadObjectFactory implementira PoolableObjectFactory {

javni Object makeObject () {vrni novo WorkerThread (); } javna void killObject (Object obj) {if (obj instanceof WorkerThread) {WorkerThread rt = (WorkerThread) obj; rt.setStopped (true); // Naredimo zaustavitev delujoče niti}} javno logično validateObject (Object obj) {if (obj instanceof WorkerThread) {WorkerThread rt = (WorkerThread) obj; if (rt.isRunning ()) {if (rt.getThreadGroup () == null) {return false; } vrni true; }} vrni true; } javna void activateObject (Object obj) {log.debug ("activateObject ..."); }

javna void passivateObject (Object obj) {log.debug ("passivateObject ..." + obj); if (obj instanceof WorkerThread) {WorkerThread wt = (WorkerThread) obj; wt.setResult (null); // Očistimo rezultat izvedbe}}}

Podrobno si oglejmo vsako metodo:

Metoda makeObject () ustvarja WorkerThread predmet. Za vsako zahtevo se področje preveri, ali je treba ustvariti nov objekt ali obstoječi objekt ponovno uporabiti. Če je na primer prva zahteva prva zahteva in je področje prazno, se ObjectPool klici za izvajanje makeObject () in doda WorkerThread do bazena.

Metoda killObject () odstrani WorkerThread objekt iz bazena z nastavitvijo logične zastavice in s tem zaustavitvijo tekoče niti. Kasneje si bomo še ogledali ta del, vendar opazimo, da zdaj prevzemamo nadzor nad tem, kako se uničujejo naši predmeti.

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