En enkel guide til tilkoblingssamling i Java

1. Oversikt

Connection pooling er et kjent datatilgangsmønster, hvis hovedformål er å redusere overhead involvert i å utføre databaseforbindelser og lese / skrive databaseaksjoner.

I et nøtteskall, et tilkoblingsbasseng er på det mest grunnleggende nivået en implementering av hurtigbuffer for databasetilkobling, som kan konfigureres for å dekke spesifikke krav.

I denne opplæringen vil vi gjøre en rask oppsummering av noen populære rammeverk for tilkoblingssamlinger, og vi lærer å implementere vår egen tilkoblingsbasseng fra bunnen av.

2. Hvorfor tilkoblingssamling?

Spørsmålet er selvfølgelig retorisk.

Hvis vi analyserer trinnsekvensen som er involvert i en typisk livssyklus for databaseforbindelse, vil vi forstå hvorfor:

  1. Åpne en forbindelse til databasen ved hjelp av databasedriveren
  2. Åpne en TCP-kontakt for å lese / skrive data
  3. Lese / skrive data over kontakten
  4. Avslutte forbindelsen
  5. Lukke stikkontakten

Det blir tydelig at databaseforbindelser er ganske dyre operasjoner, og som sådan, bør reduseres til et minimum i alle mulige brukstilfeller (i kanttilfeller, bare unngått).

Her er hvor tilkoblingspooling-implementeringer spiller inn.

Ved å bare implementere en databaseforbindelsesbeholder, som lar oss gjenbruke en rekke eksisterende tilkoblinger, kan vi effektivt spare kostnadene ved å utføre et enormt antall dyre databaseturer, og dermed øke den generelle ytelsen til våre databasedrevne applikasjoner.

3. JDBC Connection Pooling Frameworks

Fra et pragmatisk perspektiv er det bare meningsløst å implementere et tilkoblingsbasseng fra grunnen av, med tanke på antall "virksomhetsklare" rammeverk for tilkoblingspooling tilgjengelig der ute.

Fra en didaktisk, som er målet med denne artikkelen, er det ikke.

Likevel, før vi lærer hvordan vi implementerer en grunnleggende tilkoblingsbasseng, la oss først vise frem noen populære rammer for tilkoblingsbasseng.

3.1. Apache Commons DBCP

La oss starte denne raske sammendraget med Apache Commons DBCP Component, en fullverdig tilkobling som samler JDBC-rammeverk:

offentlig klasse DBCPDataSource {privat statisk BasicDataSource ds = ny BasicDataSource (); statisk {ds.setUrl ("jdbc: h2: mem: test"); ds.setUsername ("bruker"); ds.setPassword ("passord"); ds.setMinIdle (5); ds.setMaxIdle (10); ds.setMaxOpenPreparedStatements (100); } offentlig statisk Connection getConnection () kaster SQLException {return ds.getConnection (); } privat DBCPDataSource () {}}

I dette tilfellet har vi brukt en wrapper-klasse med en statisk blokk for å enkelt konfigurere DBCPs egenskaper.

Slik får du en samlet forbindelse med DBCPDataSource klasse:

Connection con = DBCPDataSource.getConnection ();

3.2. HikariCP

La oss se på HikariCP, et lynraskt JDBC-tilkoblingssamarbeid som er opprettet av Brett Wooldridge (for fullstendig informasjon om hvordan du konfigurerer og får mest mulig ut av HikariCP, vennligst sjekk denne artikkelen):

public class HikariCPDataSource {private static HikariConfig config = new HikariConfig (); privat statisk HikariDataSource ds; statisk {config.setJdbcUrl ("jdbc: h2: mem: test"); config.setUsername ("bruker"); config.setPassword ("passord"); config.addDataSourceProperty ("cachePrepStmts", "true"); config.addDataSourceProperty ("prepStmtCacheSize", "250"); config.addDataSourceProperty ("prepStmtCacheSqlLimit", "2048"); ds = ny HikariDataSource (config); } offentlig statisk Connection getConnection () kaster SQLException {return ds.getConnection (); } private HikariCPDataSource () {}}

På samme måte kan du få en samlet forbindelse med HikariCPDataSource klasse:

Connection con = HikariCPDataSource.getConnection ();

3.3. C3PO

Siste i denne anmeldelsen er C3PO, et kraftig JDBC4-tilkoblings- og statement pooling-rammeverk utviklet av Steve Waldman:

offentlig klasse C3poDataSource {private static ComboPooledDataSource cpds = new ComboPooledDataSource (); statisk {prøv {cpds.setDriverClass ("org.h2.Driver"); cpds.setJdbcUrl ("jdbc: h2: mem: test"); cpds.setUser ("bruker"); cpds.setPassword ("passord"); } fange (PropertyVetoException e) {// håndtere unntaket}} offentlig statisk Connection getConnection () kaster SQLException {return cpds.getConnection (); } privat C3poDataSource () {}}

Som forventet, få en samlet forbindelse med C3poDataSource klasse er lik de forrige eksemplene:

Connection con = C3poDataSource.getConnection ();

4. En enkel implementering

For å bedre forstå den underliggende logikken til tilkoblingssamling, la oss lage en enkel implementering.

La oss starte med et løst koblet design, basert på bare ett enkelt grensesnitt:

offentlig grensesnitt ConnectionPool {Connection getConnection (); boolsk utgivelseConnection (Connection connection); String getUrl (); String getUser (); Streng getPassword (); }

De ConnectionPool grensesnitt definerer det offentlige API-et til en grunnleggende tilkoblingsgruppe.

La oss nå lage en implementering som gir grunnleggende funksjonalitet, inkludert å få og slippe en samlet forbindelse:

offentlig klasse BasicConnectionPool implementerer ConnectionPool {private String url; privat strengbruker; privat strengpassord; privat List connectionPool; privat liste usedConnections = ny ArrayList (); privat statisk int INITIAL_POOL_SIZE = 10; offentlig statisk BasicConnectionPool create (String url, String user, String password) kaster SQLException {List pool = new ArrayList (INITIAL_POOL_SIZE); for (int i = 0; i <INITIAL_POOL_SIZE; i ++) {pool.add (createConnection (url, bruker, passord)); } returner ny BasicConnectionPool (url, bruker, passord, pool); } // standardkonstruktører @ Override public Connection getConnection () {Connection connection = connectionPool .remove (connectionPool.size () - 1); usedConnections.add (tilkobling); returforbindelse; } @ Override public boolean releaseConnection (Connection connection) {connectionPool.add (connection); returner usedConnections.remove (tilkobling); } privat statisk tilkobling createConnection (streng url, streng bruker, streng passord) kaster SQLException {return DriverManager.getConnection (url, bruker, passord); } public int getSize () {return connectionPool.size () + usedConnections.size (); } // standard getters}

Mens ganske naiv, den BasicConnectionPool klasse gir minimal funksjonalitet som vi forventer av en typisk implementering av tilkoblingssamling.

I et nøtteskall initialiserer klassen en tilkoblingsbasseng basert på en ArrayList som lagrer 10 forbindelser, som lett kan brukes på nytt.

Det er mulig å opprette JDBC-forbindelser med DriverManager klasse og med Datasource implementeringer.

Siden det er mye bedre å holde opprettelsen av forbindelsesdatabase agnostisk, har vi brukt den tidligere innen skape() statisk fabrikkmetode.

I dette tilfellet har vi plassert metoden i BasicConnectionPool, fordi dette er den eneste implementeringen av grensesnittet.

I en mer kompleks design, med flere ConnectionPool implementeringer, ville det være å foretrekke å plassere den i grensesnittet, og dermed få en mer fleksibel design og større grad av sammenheng.

Det mest relevante poenget å understreke her er at når bassenget er opprettet, tilkoblinger hentes fra bassenget, så det er ikke nødvendig å opprette nye.

Dessuten, når en forbindelse frigjøres, returneres den faktisk tilbake til bassenget, slik at andre klienter kan bruke den på nytt.

Det er ikke noe videre samspill med den underliggende databasen, for eksempel et eksplisitt kall til Tilkoblingen er nær () metode.

5. Bruke BasicConnectionPool Klasse

Som forventet, bruk vår BasicConnectionPool klasse er grei.

La oss lage en enkel enhetstest og få en samlet H2-tilkobling i minnet:

@Test offentlig nårCalledgetConnection_thenCorrect () {ConnectionPool connectionPool = BasicConnectionPool .create ("jdbc: h2: mem: test", "user", "password"); assertTrue (connectionPool.getConnection (). isValid (1)); }

6. Ytterligere forbedringer og refactoring

Selvfølgelig er det god plass til å tilpasse / utvide den nåværende funksjonaliteten til implementeringen av tilkoblingsbassenget vårt.

For eksempel kan vi refaktorere getConnection () metode, og legg til støtte for maksimal bassengstørrelse. Hvis alle tilgjengelige tilkoblinger blir tatt, og den nåværende bassengstørrelsen er mindre enn det konfigurerte maksimumet, vil metoden opprette en ny forbindelse.

Vi kan også i tillegg verifisere om tilkoblingen fra bassenget fremdeles er i live, før vi sender den til klienten.

@Override public Connection getConnection () kaster SQLException {if (connectionPool.isEmpty ()) {if (usedConnections.size () <MAX_POOL_SIZE) {connectionPool.add (createConnection (url, bruker, passord)); } annet {kast nytt RuntimeException ("Maksimal bassengstørrelse nådd, ingen tilgjengelige tilkoblinger!"); }} Tilkoblingstilkobling = connectionPool .remove (connectionPool.size () - 1); if (! connection.isValid (MAX_TIMEOUT)) {connection = createConnection (url, user, password); } usedConnections.add (tilkobling); returforbindelse; } 

Merk at metoden nå kaster SQLException, noe som betyr at vi også må oppdatere grensesnittets signatur.

Eller vi kan legge til en metode for å slå ned forekomsten av tilkoblingsbassenget:

offentlig ugyldig nedleggelse () kaster SQLException {usedConnections.forEach (dette :: releaseConnection); for (Connection c: connectionPool) {c.close (); } connectionPool.clear (); }

I produksjonsklare implementeringer, bør en tilkoblingsbasseng gi en rekke ekstra funksjoner, for eksempel muligheten for å spore tilkoblingene som for tiden er i bruk, støtte for utarbeidet pooling av uttalelser og så videre.

Siden vi holder tingene enkle, vil vi utelate hvordan vi implementerer disse tilleggsfunksjonene og holder implementeringen ikke-trådsikker for klarhets skyld.

7. Konklusjon

I denne artikkelen tok vi en grundig titt på hva tilkoblingssamling er og lærte hvordan vi kunne rulle vår egen tilkoblingssamling.

Selvfølgelig trenger vi ikke starte fra bunnen av hver gang vi ønsker å legge til et fullverdig tilkoblingsbassenglag i applikasjonene våre.

Derfor laget vi først en enkel sammendrag som viser noen av de mest populære rammene for tilkoblingsbassenger, slik at vi kan få en klar ide om hvordan vi kan jobbe med dem, og plukke opp den som passer best til våre krav.

Som vanlig er alle kodeeksemplene vist i denne artikkelen tilgjengelig på GitHub.


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