Connection Pooling in Java |
Home Page | Comments | Articles | Faq | Documents | Search | Archive | Tales from the Machine Room | Contribute | Set language to:en it | Login/Register
Durante lo sviluppo di applicazioni che accedono ad una base di dati, vi sarà capitato di notare che stabilire la connessione verso il database può richiedere anche qualche secondo.
Si tratta di un problema rilevante quando si ha a che fare con la progettazione di un sito web, le cui pagine sono costruite dinamicamente accedendo ai dati contenuti in un database.
In questo articolo vedremo come superare l'ostacolo.
Stabilire una connessione con una base di dati è un'operazione molto complessa. Infatti, è necessario caricare il driver del database, stabilire una comunicazione bidirezionale sicura fra l'applicazione che deve accedere ai dati e il database stesso, eseguire una procedura di autenticazione e allocare le risorse necessarie per gestire le query successive.
Queste operazioni sono spesso nascoste all'occhio dello sviluppatore che stabilisce la connessione con una o due chiamate di funzione. L'esecuzione di questa procedura è molto onerosa e richiede alcuni secondi per essere portata a termine.
Questo tempo è del tutto trascurabile nel caso di un'applicazione stand-alone che spesso viene mantenuta in esecuzione per tempi molto lunghi.
Nel caso di un'applicazione web le cose cambiano: lo scopo infatti è quello di gestire una singola richiesta, accedere al database e dare la risposta nel tempo minore possibile. Se per ogni richiesta non fosse necessario stabilire una nuova connessione verso il database, i tempi di risposta ne risulterebbero significativamente migliorati; inoltre il server sarebbe meno sottoposto a problemi di sovraccarico, potendo così gestire un maggior numero di visitatori.
Esiste una soluzione elegante per ridurre i tempi di connessione al database nel contesto che è stato descritto precedentemente: è sufficiente che il client inserisca la connessione in una cache invece di chiuderla quando siano terminata l'esecuzione di tutte le query.
Un altro processo che debba accedere alla base di dati può in tal caso prelevare una connessione esistente dalla cache invece di stabilirne una nuova.
Ora analizziamo una struttura dati ed una serie di funzioni minimali che ci permettano di gestire un pool di connessioni.
Per gestire le connessioni disponibili, la struttura dati migliore è una coda. In tal caso quando un processo deve accedere al database otterrà, prelevandola dalla coda, la connessione più vecchia fra quelle in cache, minimizzando la possibilità di caduta per le connessioni inutilizzate da troppo tempo.
Se non ci sono connessioni disponibili, il pool deve essere in grado di stabilirne una nuova. Pertanto deve avere a sua disposizione tutte le informazioni per farlo: generalmente si tratta di conoscere il driver, il login, la password di accesso e il nome del database.
In base a quanto detto sopra, il codice che gestisce un pool deve contenere almeno tre funzioni: getConnection per ottenere una nuova connessione prelevandola dalla coda o creandone una nuova se necessario; releaseConnection per liberare una connessione già utilizzata e garantirne il riutilizzo da parte di altri processi, inserendola nella coda delle connessioni disponibili; inoltre poiché se getConnection non trova nessuna connessione disponibile nella coda deve stabilirne una nuova, è necessario aggiungere la funzione newConnection che sia in grado di farlo quando richiesto.
Procediamo con lo sviluppo di codice Java in grado di gestire correttamente un pool di connessioni.
Costruiamo innanzitutto una classe ConnectionPoolException, le cui istanze sono le eccezioni che verranno sollevate quando si dovesse presentare un errore a runtime nella classe ConnectionPool.
// Classe che gestisce le eccezioni sollevate a runtime dalla // classe ConnectionPool public class ConnectionPoolException extends Exception { public ConnectionPoolException() { } }
Ricordiamo che al pool di connessioni dovranno accedere processi distinti. E' necessatio quindi trovare un artificio per far si che tutti i processi accedano ad unica istanza della classe ConnectionPool.
L'idea è quella di avere una variabile statica connectionPool all'interno della classe ConnectionPool. Un metodo statico, getConnectionPool, permetterà di accedere a questa variabile da parte dei processi che ne fanno richiesta. Se la variabile non è stata ancora istanziata il metodo getConnectionPool provvederà alla sua istanziazione.
Dal momento che più processi accedono a getConnectionPool in concorrenza, definiamo il metodo come synchronized.
... // La classe che gestisce un pool di connessioni public class ConnectionPool { ... // La variabile che gestisce l'unica istanza di ConnectionPool private static ConnectionPool connectionPool = null; ... public static synchronized ConnectionPool getConnectionPool() throws ConnectionPoolException { if(connectionPool == null) { connectionPool = new ConnectionPool(); } return connectionPool; } ... }
Il codice di getConnectionPool accede al costruttore della classe per creare l'unica istanza di ConnectionPool. Questo si preoccupa di creare la coda per le connessioni libere, caricare i parametri per l'accesso al database accedendo al metodo loadParameters e caricare il driver della base di dati con il metodo loadDriver.
Osserviamo che il costruttore è privato. In questo modo garantiamo che l'accesso ad esso avvenga solo tramite la funzione pubblica getConnectionPool.
import java.util.*; ... // La classe che gestisce un pool di connessioni public class ConnectionPool { ... private Vector freeConnections; // La coda di connessioni libere private String dbUrl; // Il nome del database private String dbDriver; // Il driver del database private String dbLogin; // Il login per il database private String dbPassword; // La password di accesso al database ... // Costruttore della classe ConnectionPool private ConnectionPool() throws ConnectionPoolException { // Costruisce la coda delle connessioni libere freeConnections = new Vector(); // Carica I parametric per l'accesso alla base di dati loadParameters(); // Carica il driver del database loadDriver(); } // Funzione privata che carica i parametri per l'accesso al database private loadParameters() { // Url per un database locale dbUrl = "jdbc:mysql://localhost/prova"; // Driver per database mysql dbDriver = "org.gjt.mm.mysql.Driver"; // Login della base di dati dbLogin = "Login"; // Password per l'accesso al database dbPassword = "Password"; } // Funzione privata che carica il driver per l'accesso al database. // In caso di errore durante il caricamento del driver solleva un'eccezione. private loadDriver() throws ConnectionPoolException { try { java.lang.Class.forName( dbDriver + "?user=" + dbUser + "&password=" + dbPassword); } catch (Exception e) { throw new ConnectionPoolException(); } } ... }
Analizziamo la funzione getConnection che restituisce una connessione libera. getConnection verifica innanzitutto che ci siano elementi nella coda delle connessioni libere. Se la coda non è vuota, preleva la prima connessione e verifica che sia valida. Se la connessione non è più valida effettua una chiamata ricorsiva per prelevare l'elemento successivo.
Se la coda e' vuota, getConnection chiama newConnection per stabilire una nuova connessione. Il metodo getConnection è di tipo synchronized perché può essere richiamato da più processi concorrenti.
Il metodo newConnection non deve necessariamente essere synchronized perché viene richiamato da getConnection che già opera in mutua esclusione.
import java.sql.*; ... // Il metodo getConnection restituisce una connessione libera prelevandola // dalla coda freeConnections oppure se non ci sono connessioni disponibili // creandone una nuova con una chiamata a newConnection public synchronized Connection getConnection() throws ConnectionPoolException { Connection con; if(freeConnections.size() > 0) { // Se la coda delle connessioni libere non è vuota // preleva il primo elemento e lo rimuove dalla coda con = (Connection) freeConnections.firstElement(); freeConnections.removeElementAt(0); try { // Verifica se la connessione non è più valida if(con.isClosed()) { // Richiama getConnection ricorsivamente con = getConnection(); } } catch(SQLException e) { // Se c'è un errore richiama GetConnection // ricorsivamente con = getConnection(); } } else { // se la coda delle connessioni libere è vuota // crea una nuova connessione con = newConnection(); } // restituisce la connessione return con; } // Il metodo newConnection restituisce una nuova connessione private Connection newConnection() throws ConnectionPoolException { Connection con = null; try { // crea la connessione con = DriverManager.getConnection(dbUrl); } catch(SQLException e) { // in caso di errore solleva un'eccezione throw new ConnectionPoolException(); } // restituisce la nuova connessione return con; } ...
Una volta terminato l'uso della connessione si chiama il metodo releaseConnection per ritornare la connessione non più utilizzata nella coda.
Questo metodo come getConnection opera in regime di concorrenza e deve quindi essere synchronized.
... // Il metodo releaseConnection rilascia una connessione inserendola // nella coda delle connessioni libere public synchronized void releaseConnection(Connection con) { // Inserisce la connessione nella coda freeConnections.add(con); } ..
Accesso ai database da java:
Java database e programmazione client/server - Giuseppe Naccarato - Apogeo
Java 2 Tecniche avanzate - Cay S. Horstmann e Gary Cornwell - McGraw Hill
Tecniche per gestire eccezioni e per la programmazione concorrente:
Java la guida completa - Patrick Naughton e Herbert Schildt - Mc Graw Hill
Core Java - Gary Cornell e Cay S. Horstmann - Sunsoft Press
Java Fondamenti di programmazione - Deitel & Deitel - Apogeo
Sviluppo di siti web dinamici tramite servlet:
Java Servlet Programmino - Jason Hunter e Williiam Crawford - O'Reilly
Accesso a database MySql da codice Java:
JSP Servlet e MySql - David Harms - McGraw Hill
Comments are added when and more important if I have the time to review them and after removing Spam, Crap, Phishing and the like. So don't hold your breath. And if your comment doesn't appear, is probably becuase it wasn't worth it.
riccardo By riccardo posted 22/04/2008 12:52
lorenzo By lorenzo posted 12/06/2008 11:55
dbcp in jakarta e c3p0, alcuni orm supportano direttamente il pooling(hibernate)
DoktorVis By DoktorVis posted 23/09/2008 22:35
stecolna By stecolna posted 24/09/2008 09:26
W il potere della programmazione ad oggetti!!!!
massi By massi posted 02/10/2008 10:52
i commenti non sono un forum, leggi la documentazione di quel coso o domanda sul loro forum di supporto.
uno qualsiasi By uno qualsiasi posted 13/11/2008 13:23
Davide Lorenzo Marino, residente a Roma, e' esperto di programmazione Java, Visual Basic, C/C++, Pascal, Perl ed altri linguaggi piu' o meno noti. Attualmente e' in cerca di occupazione (possibilmente come programmatore Java, possibilmente in Roma).
Do you want to contribute?
read how.
This site is made by me with blood, sweat and gunpowder, if you want to republish or redistribute any part of it, please drop me (or the author of the article if is not me) a mail.
This site was composed with VIM, now is composed with VIM and the (in)famous CMS FdT.
This site isn't optimized for vision with any specific browser, nor
it requires special fonts or resolution.
You're free to see it as you wish.