Ohjelmointi

Yhdistä resurssit Apache's Commons Pool Framework -sovelluksella

Resurssien yhdistäminen (kutsutaan myös esineiden yhdistämiseksi) useiden asiakkaiden keskuudessa on tekniikka, jota käytetään objektien uudelleenkäytön edistämiseen ja uusien resurssien luomisen yleiskustannusten vähentämiseen, mikä johtaa parempaan suorituskykyyn ja suorituskykyyn. Kuvittele raskas Java-palvelinsovellus, joka lähettää satoja SQL-kyselyjä avaamalla ja sulkemalla yhteydet jokaiselle SQL-pyynnölle. Tai Web-palvelin, joka palvelee satoja HTTP-pyyntöjä käsittelemällä jokaista pyyntöä kutemalla erillinen säie. Tai kuvittele, että luodaan XML-jäsennin-ilmentymä jokaiselle asiakirjaan liittyvälle pyynnölle käyttämättä instansseja uudelleen. Nämä ovat joitain skenaarioita, jotka edellyttävät käytettävien resurssien optimointia.

Resurssien käyttö voi osoittautua ajoittain kriittiseksi raskaissa sovelluksissa. Jotkut kuuluisat verkkosivustot ovat lopettaneet, koska ne eivät kykene käsittelemään raskaita kuormia. Suurin osa raskaisiin kuormituksiin liittyvistä ongelmista voidaan hoitaa makrotasolla klusterointi- ja kuormituksen tasapainotusominaisuuksien avulla. Sovellustasolla on edelleen huolta liiallisesta objektinluonnista ja rajoitettujen palvelinresurssien, kuten muistin, suorittimen, ketjujen ja tietokantayhteyksien saatavuudesta, mikä saattaa edustaa potentiaalisia pullonkauloja ja, ellei sitä käytetä optimaalisesti, kaataa koko palvelimen.

Joissakin tilanteissa tietokannan käyttöpolitiikka voi asettaa rajoituksen samanaikaisten yhteyksien määrälle. Myös ulkoinen sovellus voi sanella tai rajoittaa samanaikaisten avoimien yhteyksien määrää. Tyypillinen esimerkki on verkkotunnusrekisteri (kuten Verisign), joka rajoittaa rekisteröijille (kuten BulkRegister) saatavissa olevien aktiivisten pistorasioiden yhteyksien määrää. Resurssien yhdistäminen on osoittautunut yhdeksi parhaista vaihtoehdoista tämäntyyppisten ongelmien käsittelyssä ja auttaa jossain määrin myös ylläpitämään vaadittuja palvelutasoja yrityssovelluksille.

Useimmat J2EE-sovelluspalvelimien toimittajat tarjoavat resurssien yhdistämisen kiinteänä osana Web- ja EJB (Enterprise JavaBean) -säilöitään. Tietokantayhteyksiä varten palveluntarjoaja toimittaa yleensä Tietolähde käyttöliittymä, joka toimii yhdessä JDBC (Java Database Connectivity) -ajuritoimittajan kanssa ConnectionPoolDataSource toteutus. ConnectionPoolDataSource toteutus toimii resurssienhallinnan yhteystehtaana yhdistetyille java.sql.Connection esineitä. Samoin EJB-tapaukset, joissa on valtiottomia istuntopapuja, viestiohjattuja papuja ja kokonaisuuspapuja, yhdistetään EJB-säiliöihin paremman suorituskyvyn ja suorituskyvyn saavuttamiseksi. XML-jäsennin-esiintymät ovat myös ehdokkaita yhdistämiselle, koska jäsentäjien esiintymien luominen vie suuren osan järjestelmän resursseista.

Menestyksekäs avoimen lähdekoodin resurssien yhdistämistoteutus on Commons Pool Frameworkin DBCP, Apace Software Foundationin tietokantayhteyksien yhdistämiskomponentti, jota käytetään laajasti tuotantoluokan yrityssovelluksissa. Tässä artikkelissa keskustelen lyhyesti Commons Pool -kehyksen sisäisistä osista ja käytän sitä sitten ketjupoolin toteuttamiseen.

Katsotaan ensin, mitä kehys tarjoaa.

Commons Pool -kehys

Commons Pool -kehys tarjoaa perustason ja vankan toteutuksen mielivaltaisten objektien yhdistämiseksi. Tarjolla on useita toteutuksia, mutta tämän artikkelin tarkoituksiin käytämme yleisintä toteutusta GenericObjectPool. Se käyttää a CursorableLinkedList, joka on kaksinkertaisesti linkitetyn luettelon toteutus (osa Jakarta Commons -kokoelmia), pohjana olevana tietorakenteena yhdistettävien objektien pitämiseen.

Lisäksi kehys tarjoaa joukon rajapintoja, jotka tarjoavat elinkaarimenetelmiä ja auttajamenetelmiä poolin hallintaan, seurantaan ja laajentamiseen.

Käyttöliittymä org.apache.commons.PoolableObjectFactory määrittelee seuraavat elinkaarimenetelmät, jotka osoittautuvat välttämättömiksi yhdistämiskomponentin toteuttamiseksi:

 // Luo ilmentymän, jonka pooli voi palauttaa julkisen objektin makeObject () {} // Tuhoaa ilmentymän, jota pooli ei enää tarvitse julkinen void destileObject (Object obj) {} // Vahvista objekti, ennen kuin käytät sitä julkisen boolean validateObject (Object obj) {} // Alusta kopion palauttama ilmentymä public void activObject (Object obj) {} // Poista alusta kopio, joka palautetaan poolin public void passivateObject (Object obj) {}

Kuten voit tehdä menetelmäallekirjoituksilla, tämä käyttöliittymä käsittelee ensisijaisesti seuraavaa:

  • makeObject (): Toteuttaa objektin luonti
  • destrObject (): Toteuta kohteen tuhoaminen
  • validateObject (): Vahvista objekti ennen sen käyttöä
  • activObject (): Ota käyttöön objektin alustuskoodi
  • passivateObject (): Ota käyttöön objektin alustamisen koodi

Toinen ydinliitäntä -org.apache.commons.ObjectPool—Määrittelee seuraavat menetelmät varaston hallinnoimiseksi ja valvomiseksi:

 // Hanki ilmentymä poolistani Object borrowObject () heittää Exception; // Palauta ilmentymä pooliin void returnObject (Object obj) heittää Exception; // Mitätöi objektin poolista void invalidateObject (Object obj) heittää Exception; // Käytetään tyhjän objektin sisältävän altaan esilataamiseen void addObject () throws Exception; // Palauta käyttämättömien instanssien lukumäärä int getNumIdle () heittää UnsupportedOperationException; // Palauttaa aktiivisten instanssien lukumäärän int getNumActive () heittää UnsupportedOperationException; // Tyhjentää käyttämättömät objektit void clear () heittää Exception, UnsupportedOperationException; // Sulje allas void close () heittää Exception; // Aseta ObjectFactory, jota käytetään ilmentymien luomiseen void setFactory (PoolableObjectFactory factory) heittää IllegalStateException, UnsupportedOperationException;

ObjectPool käyttöliittymän toteuttaminen vie a PoolableObjectFactory argumenttina konstruktoreissaan, jolloin objektin luominen delegoidaan alakategorioihinsa. En puhu paljon suunnittelumalleista täällä, koska se ei ole meidän painopisteemme. Lukijoille, jotka ovat kiinnostuneita tarkastelemaan UML-luokkakaavioita, katso Resurssit.

Kuten edellä mainittiin, luokka org.apache.commons.GenericObjectPool on vain yksi org.apache.commons.ObjectPool käyttöliittymä. Kehys tarjoaa myös toteutuksia avainasemakohteille käyttöliittymiä käyttämällä org.apache.commons.KeyedObjectPoolFactory ja org.apache.commons.KeyedObjectPool, jossa allas voidaan liittää avaimeen (kuten HashMap) ja siten hallita useita pooleja.

Avain onnistuneeseen poolointistrategiaan riippuu siitä, miten me määrittelemme poolin. Huonosti määritetyt altaat voivat olla resurssisikoja, jos kokoonpanoparametreja ei ole viritetty oikein. Katsotaanpa joitain tärkeitä parametreja ja niiden tarkoitusta.

Kokoonpanon tiedot

Allas voidaan määrittää käyttämällä GenericObjectPool.Config luokka, joka on staattinen sisempi luokka. Vaihtoehtoisesti voisimme käyttää vain GenericObjectPoolsetterien määritysmenetelmät arvojen asettamiseksi.

Seuraava luettelo sisältää joitain käytettävissä olevia kokoonpanoparametreja GenericObjectPool toteutus:

  • maxIdle: Nukkumisten enimmäismäärä uima-altaassa ilman ylimääräisiä esineitä.
  • minIdle: Nukkumisten vähimmäismäärä altaassa ilman ylimääräisten esineiden luomista.
  • maxActive: Aktiivisten ilmentymien enimmäismäärä poolissa.
  • timeBetweenEvictionRunsMillis: Millisekuntien lukumäärä lepotilassa olevan objektin haihdutuslangan ajon välillä. Kun negatiivinen, tyhjäkäynnin ja objektin hävittäjälanka ei toimi. Käytä tätä parametria vain, kun haluat, että haihdutuslanka suoritetaan.
  • minEvictableIdleTimeMillis: Kohteen vähimmäisaika, jos se on aktiivinen, voi istua tyhjäkäynnillä uima-altaassa, ennen kuin se on kelvollinen tyhjäkäynnillä olevan objektin häätölaitteella. Jos syötetään negatiivinen arvo, esineitä ei häädetä pelkästään joutokäynnin vuoksi.
  • testOnBorrow: Kun "true" -objektit tarkistetaan. Jos objektin vahvistus epäonnistuu, se pudotetaan altaasta ja pool yrittää lainata toista.

Yllä oleville parametreille tulisi antaa optimaaliset arvot maksimaalisen suorituskyvyn ja suorituskyvyn saavuttamiseksi. Koska käyttötapa vaihtelee sovelluksesta toiseen, viritä varasto eri parametriyhdistelmillä optimaalisen ratkaisun saavuttamiseksi.

Ymmärrämme lisää altaasta ja sen sisäosista ottamalla käyttöön lanka-allas.

Ehdotetut kierreallasvaatimukset

Oletetaan, että meitä kehotettiin suunnittelemaan ja toteuttamaan ketjupoolikomponentti työn ajoittajalle käynnistämään työt määrätyillä aikatauluilla ja raportoimaan suorituksen loppuun saattaminen ja mahdollisesti tulos. Tällaisessa tilanteessa ketjujoukkomme tavoitteena on yhdistää vaadittu määrä ketjuja ja suorittaa ajoitetut työt itsenäisissä säikeissä. Vaatimukset on tiivistetty seuraavasti:

  • Lankan tulisi pystyä kutsumaan mikä tahansa mielivaltainen luokkatapa (ajoitettu työ)
  • Langan pitäisi pystyä palauttamaan suorituksen tulos
  • Lankan tulisi pystyä ilmoittamaan tehtävän suorittamisesta

Ensimmäinen vaatimus tarjoaa mahdollisuuden löyhästi kytkettyyn toteutukseen, koska se ei pakota meitä toteuttamaan sellaista käyttöliittymää kuin Ajettava. Se tekee myös integroinnista helppoa. Voimme toteuttaa ensimmäisen vaatimuksen toimittamalla ketjulle seuraavat tiedot:

  • Luokan nimi
  • Menetelmän nimi, jota käytetään
  • Menetelmälle välitettävät parametrit
  • Lähetettyjen parametrien parametrityypit

Toinen vaatimus sallii ketjun käyttävän asiakkaan saada suoritustuloksen. Yksinkertainen toteutus olisi tallentaa suorituksen tulos ja tarjota käyttöoikeusmenetelmä, kuten getResult ().

Kolmas vaatimus liittyy jonkin verran toiseen vaatimukseen. Tehtävän suorittamisen ilmoittaminen voi tarkoittaa myös sitä, että asiakas odottaa suorituksen tuloksen saamista. Tämän ominaisuuden käsittelemiseksi voimme tarjota jonkinlaisen takaisinsoittomekanismin. Yksinkertaisin takaisinsoittomekanismi voidaan toteuttaa käyttämällä java.lang.objektion odota() ja ilmoittaa() semantiikka. Vaihtoehtoisesti voisimme käyttää Tarkkailija mutta pidetään nyt asiat yksinkertaisina. Saatat olla kiusaus käyttää java.lang.Thread luokan liittyä seuraan() menetelmä, mutta se ei toimi, koska yhdistetty ketju ei koskaan viimeistele sitä juosta() menetelmää ja jatkuu niin kauan kuin allas sitä tarvitsee.

Nyt kun vaatimuksemme ovat valmiit ja karkea idea ketjupoolin toteuttamisesta, on aika tehdä todellinen koodaus.

Tässä vaiheessa ehdotetun mallin UML-luokkakaavio näyttää alla olevalta kuvalta.

Langankierteen toteuttaminen

Lankaobjekti, jonka aiomme yhdistää, on itse asiassa kääre lankaobjektin ympärille. Soitetaan kääre TyöntekijäKierre luokka, joka laajentaa java.lang.Thread luokassa. Ennen kuin voimme aloittaa koodauksen TyöntekijäKierre, meidän on pantava täytäntöön kehysvaatimukset. Kuten aiemmin näimme, meidän on pantava täytäntöön PoolableObjectFactory, joka toimii tehtaana, luoda yhdistettävämme TyöntekijäKierres. Kun tehdas on valmis, toteutamme ThreadPool laajentamalla GenericObjectPool. Sitten olemme valmiit TyöntekijäKierre.

PoolableObjectFactory-käyttöliittymän toteuttaminen

Aloitamme PoolableObjectFactory käyttöliittymässä ja yritä toteuttaa tarvittavat elinkaarimenetelmät ketjupoolissamme. Kirjoitamme tehdasluokan ThreadObjectFactory seuraavasti:

public class ThreadObjectFactory toteuttaa PoolableObjectFactory {

public Object makeObject () {return new WorkerThread (); } public void destrObject (Object obj) {if (WorkerThreadin obj instance) {WorkerThread rt = (WorkerThread) obj; rt.setStopped (true); // Tee käynnissä oleva säie lopetettavaksi}} julkinen totuusarvo validateObject (Object obj) {if (objektin WorkerThread) {WorkerThread rt = (WorkerThread) obj; if (rt.isRunning ()) {if (rt.getThreadGroup () == null) {palauta epätosi; } return true; }} return true; } public void activObject (Object obj) {log.debug ("activObject ..."); }

public void passivateObject (Object obj) {log.debug ("passivateObject ..." + obj); if (obj esimerkki WorkerThread) {WorkerThread wt = (WorkerThread) obj; wt.setResult (nolla); // Siivoa suorituksen tulos}}}

Käydään läpi jokainen menetelmä yksityiskohtaisesti:

Menetelmä makeObject () luo TyöntekijäKierre esine. Jokaisen pyynnön yhteydessä allas tarkistetaan, onko uusi objekti luotava vai onko olemassa olevaa objektia tarkoitus käyttää uudelleen. Esimerkiksi, jos tietty pyyntö on ensimmäinen pyyntö ja allas on tyhjä, ObjectPool täytäntöönpanopyynnöt makeObject () ja lisää TyöntekijäKierre uima-altaalle.

Menetelmä destrObject () poistaa TyöntekijäKierre objekti uima-altaasta asettamalla Boolen-lippu ja pysäyttämällä juokseva lanka. Katsomme tätä kappaletta myöhemmin uudelleen, mutta huomaa, että otamme nyt hallinnan siitä, miten esineemme tuhotaan.

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