Programmēšana

Resursu apvienošana, izmantojot Apache's Commons Pool Framework

Resursu apvienošana (saukta arī par objektu apvienošanu) starp vairākiem klientiem ir paņēmiens, ko izmanto, lai veicinātu objektu atkārtotu izmantošanu un samazinātu jaunu resursu radīšanas izmaksas, kā rezultātā tiek uzlabota veiktspēja un caurlaidspēja. Iedomājieties lieljaudas Java servera lietojumprogrammu, kas nosūta simtiem SQL vaicājumu, atverot un aizverot savienojumus katram SQL pieprasījumam. Vai tīmekļa serveris, kas apkalpo simtiem HTTP pieprasījumu, apstrādāot katru pieprasījumu, radot atsevišķu pavedienu. Vai arī iedomājieties izveidot XML parsētāja instanci katram pieprasījumam parsēt dokumentu, atkārtoti neizmantojot instances. Šie ir daži no scenārijiem, kas pamato izmantoto resursu optimizāciju.

Resursu izmantošana reizēm var izrādīties kritiska lieljaudas lietojumprogrammām. Dažas slavenās vietnes ir slēgtas, jo nespēj izturēt lielas slodzes. Lielāko daļu problēmu, kas saistītas ar lielām slodzēm, var risināt makro līmenī, izmantojot kopu veidošanas un slodzes līdzsvarošanas iespējas. Lietojumprogrammu līmenī joprojām pastāv bažas par pārmērīgu objektu izveidi un ierobežotu servera resursu, piemēram, atmiņas, centrālā procesora, pavedienu un datu bāzes savienojumu pieejamību, kas varētu radīt iespējamās vājās vietas un, ja tās netiek optimāli izmantotas, sagraut visu serveri.

Dažās situācijās datu bāzes izmantošanas politika varētu noteikt vienlaicīgu savienojumu skaita ierobežojumu. Arī ārēja lietojumprogramma varētu diktēt vai ierobežot vienlaicīgi atvērto savienojumu skaitu. Tipisks piemērs ir domēna reģistrs (piemēram, Verisign), kas ierobežo reģistratoriem pieejamo aktīvo ligzdu savienojumu skaitu (piemēram, BulkRegister). Resursu apvienošana ir izrādījusies viena no labākajām iespējām šāda veida problēmu risināšanā un zināmā mērā palīdz arī uzturēt nepieciešamo pakalpojumu līmeni uzņēmuma lietojumprogrammām.

Lielākā daļa J2EE lietojumprogrammu serveru piegādātāju nodrošina resursu apvienošanu kā neatņemamu Web un EJB (Enterprise JavaBean) konteineru sastāvdaļu. Datu bāzes savienojumiem servera piegādātājs parasti nodrošina Datu avots saskarne, kas darbojas kopā ar JDBC (Java Database Connectivity) draiveru pārdevēju ConnectionPoolDataSource ieviešana. The ConnectionPoolDataSource ieviešana kalpo kā resursu pārvaldnieka savienojumu rūpnīca apvienotajiem java.sql.Savienojums objektiem. Līdzīgi EJB gadījumi, kad sesiju pupas bez valstspiederības, uz ziņām balstītas pupiņas un entītijas pupiņas tiek apvienotas EJB konteineros, lai nodrošinātu lielāku caurlaidspēju un veiktspēju. XML parsētāja gadījumi ir arī apvienošanās kandidāti, jo parsētāja gadījumu izveide patērē lielu daļu sistēmas resursu.

Veiksmīga atvērtā koda resursu apvienošanas ieviešana ir Commons Pool ietvara DBCP - datu bāzes savienojumu apvienošanas komponents no Apace Software Foundation, ko plaši izmanto ražošanas klases uzņēmumu lietojumprogrammās. Šajā rakstā es īsi apspriedu Commons Pool ietvara iekšējos elementus un pēc tam tos izmantoju, lai ieviestu pavedienu kopu.

Vispirms apskatīsim, ko nodrošina sistēma.

Commons Pool sistēma

Commons Pool ietvars piedāvā vienkāršu un stabilu ieviešanu patvaļīgu objektu apvienošanai. Tiek piedāvātas vairākas realizācijas, taču šī raksta vajadzībām mēs izmantojam visizplatītāko ieviešanu - GenericObjectPool. Tas izmanto a CursorableLinkedList, kas ir divkārši saistīta saraksta ieviešana (daļa no Džakartas kopienas kolekcijas) kā pamatā esošā datu struktūra strukturēto objektu glabāšanai.

Turklāt ietvars nodrošina saskarņu kopumu, kas nodrošina dzīves cikla metodes un palīgmetodes pūla pārvaldībai, uzraudzībai un paplašināšanai.

Saskarne org.apache.commons.PoolableObjectFactory definē šādas dzīves cikla metodes, kas izrādās būtiskas apvienošanas komponenta ieviešanai:

 // Izveido gadījumu, kuru var atdot kopas publiskais objekts makeObject () {} // Iznīcina instanci, kas pūlam vairs nav vajadzīgs publiskais void destilētObject (Object obj) {} // Apstipriniet objektu pirms tā izmantošanas publiskā būla validateObject (Object obj) {} // Inicializējiet instanci, kuru atdod baseins public void activObject (Object obj) {} // Uninicializē instanci, kas jāatgriež baseina public void passivateObject (Object obj) {}

Kā jūs varat noskaidrot ar parakstu metodi, šī saskarne galvenokārt attiecas uz:

  • makeObject (): Īstenojiet objekta izveidi
  • iznīcināt objektu (): Īstenojiet objekta iznīcināšanu
  • validateObject (): Apstipriniet objektu pirms tā izmantošanas
  • activObject (): Ieviesiet objekta inicializācijas kodu
  • passivateObject (): Ieviesiet objekta inicializācijas kodu

Vēl viena galvenā saskarne -org.apache.commons.ObjectPool—Definē šādas kopas pārvaldīšanas un uzraudzības metodes:

 // Iegūstiet instanci no mana baseina Object borrowObject () throws Exception; // Atgriezt instanci manam baseinam void returnObject (Object obj) throws Exception; // Invalidē objektu no kopas void invalidateObject (Object obj) throws Exception; // Izmanto, lai iepriekš ielādētu baseinu ar dīkstāves objektiem void addObject () throws Exception; // Atgrieziet dīkstāves gadījumu skaitu int getNumIdle () throws UnsupportedOperationException; // Atgriež aktīvo gadījumu skaitu int getNumActive () throws UnsupportedOperationException; // Notīra tukšgaitas objektus void clear () throws Exception, UnsupportedOperationException; // Aizveriet baseinu void close () throws Exception; // Iestatiet ObjectFactory, kas jāizmanto, lai izveidotu instances void setFactory (rūpnīca PoolableObjectFactory) throws IllegalStateException, UnsupportedOperationException;

The ObjectPool saskarnes ieviešana prasa a PoolableObjectFactory kā argumentu tā konstruktoros, tādējādi objektu radīšanu deleģējot savām apakšklasēm. Es šeit daudz nerunāju par dizaina modeļiem, jo ​​tas nav mūsu uzmanības centrā. Lasītājiem, kuri vēlas apskatīt UML klases diagrammas, lūdzu, skatiet resursus.

Kā minēts iepriekš, klase org.apache.commons.GenericObjectPool ir tikai viena programmas īstenošana org.apache.commons.ObjectPool interfeiss. Sistēma nodrošina arī atslēgas objektu kopu ieviešanu, izmantojot saskarnes org.apache.commons.KeyedObjectPoolFactory un org.apache.commons.KeyedObjectPool, kur var saistīt baseinu ar atslēgu (kā HashMap) un tādējādi pārvaldīt vairākus baseinus.

Veiksmīgas apvienošanas stratēģijas atslēga ir atkarīga no tā, kā mēs konfigurēsim baseinu. Slikti konfigurēti baseini var būt resursu cūkas, ja konfigurācijas parametri nav pareizi noregulēti. Apskatīsim dažus svarīgus parametrus un to mērķi.

Informācija par konfigurāciju

Baseinu var konfigurēt, izmantojot GenericObjectPool.Config klase, kas ir statiska iekšējā klase. Alternatīvi mēs varētu vienkārši izmantot GenericObjectPoolsetter metodes, lai iestatītu vērtības.

Šajā sarakstā ir sīki aprakstīti daži no sistēmas konfigurācijai pieejamajiem parametriem GenericObjectPool ieviešana:

  • maxIdle: Maksimālais gulēšanas gadījumu skaits baseinā bez papildu priekšmetu atbrīvošanas.
  • min. dīkstāvē: Minimālais gulēšanas gadījumu skaits baseinā bez papildu objektu izveidošanas.
  • maxActive: Maksimālais aktīvo gadījumu skaits pūlā.
  • timeBetweenEvictionRunsMillis: Miega sekunžu skaits gulēšanai starp dīkstāves objekta izšļakstīšanas pavediena izpildi. Ja vērtība ir negatīva, nedarbosies neviens tukšgaitas objekta izdzēšanas pavediens. Izmantojiet šo parametru tikai tad, ja vēlaties, lai palaistu izdzinēja pavediens.
  • minEvictableIdleTimeMillis: Minimālais laiks, kamēr objekts, ja tas ir aktīvs, var sēdēt dīkstāvē baseinā, pirms to var izlikt no dīkstāves objekta izlikšanas. Ja tiek sniegta negatīva vērtība, tikai dīkstāves dēļ neviens objekts netiek izlikts.
  • testOnBorrow: Kad vērtība ir “true”, objekti tiek pārbaudīti. Ja objekta validācija neizdodas, tas tiks izmests no kopas un baseins mēģinās aizņemties citu.

Lai sasniegtu maksimālu veiktspēju un caurlaidspēju, iepriekšminētajiem parametriem ir jānodrošina optimālas vērtības. Tā kā lietošanas veids dažādās lietojumprogrammās ir atšķirīgs, pielāgojiet pūli ar dažādām parametru kombinācijām, lai panāktu optimālu risinājumu.

Lai vairāk izprastu par baseinu un tā iekšējām daļām, ieviesīsim pavedienu kopu.

Piedāvātās vītņu kopas prasības

Pieņemsim, ka mums lika izstrādāt un ieviest pavedienu kopas komponentu darba plānotājam, lai aktivizētu darbus pēc noteiktiem grafikiem un ziņotu par izpildes pabeigšanu un, iespējams, par izpildes rezultātu. Šādā gadījumā mūsu pavedienu kopas mērķis ir apvienot nepieciešamo pavedienu skaitu un veikt ieplānotos darbus neatkarīgos pavedienos. Prasības ir apkopotas šādi:

  • Vītnei jāspēj izsaukt jebkuru patvaļīgu klases metodi (ieplānoto darbu)
  • Vītnei vajadzētu būt iespējai atgriezt izpildes rezultātu
  • Vītnei vajadzētu būt iespējai ziņot par uzdevuma izpildi

Pirmā prasība paredz brīvi savienotas ieviešanas iespējas, jo tā neliek mums ieviest tādu saskarni kā Skrienams. Tas arī atvieglo integrāciju. Mēs varam īstenot savu pirmo prasību, sniedzot pavedienam šādu informāciju:

  • Nodarbības nosaukums
  • Izmantojamās metodes nosaukums
  • Parametri, kas jānodod metodei
  • Nosūtīto parametru parametru veidi

Otrā prasība ļauj klientam, kurš izmanto pavedienu, saņemt izpildes rezultātu. Vienkārša ieviešana būtu izpildes rezultāta saglabāšana un tādas piekļuves metodes nodrošināšana kā getResult ().

Trešā prasība ir nedaudz saistīta ar otro prasību. Ziņošana par uzdevuma izpildi var nozīmēt arī to, ka klients gaida izpildes rezultātu. Lai apstrādātu šo iespēju, mēs varam nodrošināt kāda veida atzvanīšanas mehānismu. Vienkāršāko atzvanīšanas mehānismu var ieviest, izmantojot java.lang.Object's pagaidiet () un paziņot() semantika. Alternatīvi mēs varētu izmantot Novērotājs bet tagad saglabāsim lietas vienkāršas. Jums varētu rasties kārdinājums izmantot java.lang.Thread klases pievienoties () metodi, taču tas nedarbosies, jo apvienotais pavediens nekad to nepabeidz palaist () metodi un turpina darboties, kamēr baseinam tas ir vajadzīgs.

Tagad, kad mums ir gatavas prasības un aptuvena ideja par pavedienu kopas ieviešanu, ir pienācis laiks veikt reālu kodēšanu.

Šajā posmā mūsu piedāvātā dizaina UML klases diagramma izskatās kā attēlā zemāk.

Vītņu kopas ieviešana

Vītnes objekts, kuru mēs apvienosim, faktiski ir aptinums ap pavediena objektu. Sauksim iesaiņotāju WorkerThread klase, kas pagarina java.lang.Thread klasē. Pirms mēs varam sākt kodēšanu WorkerThread, mums jāievieš pamatprasības. Kā mēs redzējām iepriekš, mums jāievieš PoolableObjectFactory, kas darbojas kā rūpnīca, lai izveidotu mūsu apvienojamo WorkerThreads. Kad rūpnīca ir gatava, mēs ieviešam ThreadPool pagarinot GenericObjectPool. Tad mēs pabeidzim savu WorkerThread.

PoolableObjectFactory saskarnes ieviešana

Mēs sākam ar PoolableObjectFactory saskarni un mēģiniet ieviest nepieciešamās dzīves cikla metodes mūsu pavedienu kopai. Mēs rakstām rūpnīcas klasi ThreadObjectFactory sekojoši:

publiskā klase ThreadObjectFactory īsteno PoolableObjectFactory {

public Object makeObject () {return new WorkerThread (); } public void destrObject (Object obj) {if (WorkerThread obj objekts) {WorkerThread rt = (WorkerThread) obj; rt.setStopped (true); // Padarīt skrienošo pavedienu stop}} publisku boolean validateObject (Object obj) {if (obj instanceof WorkerThread) {WorkerThread rt = (WorkerThread) obj; if (rt.isRunning ()) {if (rt.getThreadGroup () == null) {return false; } return true; }} return true; } public void activObject (Object obj) {log.debug ("activObject ..."); }

public void passivateObject (Object obj) {log.debug ("passivateObject ..." + obj); if (obj instanceof WorkerThread) {WorkerThread wt = (WorkerThread) obj; wt.setResult (null); // Notīrīt izpildes rezultātu}}}

Apskatīsim katru metodi detalizēti:

Metode makeObject () rada WorkerThread objekts. Katram pieprasījumam tiek pārbaudīts kopa, lai redzētu, vai ir jāizveido jauns objekts vai atkārtoti jāizmanto esošs objekts. Piemēram, ja konkrēts pieprasījums ir pirmais pieprasījums un kopa ir tukša, ObjectPool ieviešanas zvani makeObject () un pievieno WorkerThread uz baseinu.

Metode iznīcināt objektu () noņem WorkerThread objektu no baseina, iestatot Būla karodziņu un tādējādi apturot darbojošos pavedienu. Mēs vēlāk apskatīsim šo gabalu vēlreiz, taču ievērojiet, ka tagad mēs pārņemam kontroli pār to, kā tiek iznīcināti mūsu objekti.

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