1. Einführung
Die Pooling -Technologie wird in Java weit verbreitet. Kurz gesagt, es verwendet einen Objektpool, um eine Instanz mit begrenzter Anzahl von Instanzen zu speichern. Entwickler erhalten Instanzen aus dem Objektpool und wechseln nach der Verwendung wieder zum Objektpool zurück zum Objektpool, wodurch der Aufwand der häufigen Erstellung von Objekten des Systems und die Zerstörung von Objekten in gewissem Maße verringert wird. Der Java -Threadpool und der Datenbankverbindungspool sind typische Anwendungen, aber nicht alle Objekte sind zum Pooling geeignet. Das Pooling von Objekten mit relativ kleinem Overhead wirkt sich auf die Leistung aus, da die Wartung von Objektpools auch einen bestimmten Ressourcenaufwand erfordert. Bei Objekten mit hohem Overhead und häufiger Erstellung und Nutzung wird die Verwendung von Pooling -Technologie die Leistung erheblich verbessern.
Es gibt viele reife Datenbankverbindungspools in der Branche, wie C3P0, DBCP, Proxool und Alibabas Druid. Es gibt viele und Open Source, und Sie können Quellcode auf GitHub finden. Entwickler können basierend auf ihren eigenen Bedürfnissen und ihren Eigenschaften und Leistung wählen. In diesem Artikel geht es nur darum, die Lernpooling -Technologie zu verstehen und einen einfachen Datenbankverbindungspool zu implementieren. Wenn es Fehler gibt, hoffe ich, sie zu kritisieren und zu korrigieren.
2. Design
Hauptklassen und Schnittstellen
.ConnectionParam - Parameterklasse für Datenbankverbindungspool, verantwortlich für die Konfiguration von Datenbankverbindungen und den zu verbundenen Parametern des Verbindungspools. Implementierung mit Builder.
URL -Benutzerkennwort für Treiber - erforderlich, um eine Verbindung zur Datenbank herzustellen
Minconnection - Mindestanzahl von Verbindungen
MaxConnection - Maximale Anzahl von Verbindungen
Minidle - Mindestanzahl von Leerlaufverbindungen
Maxwait - Maximale Wartezeit
privater Final String -Treiber; private endgültige String -URL; private endgültige String -Benutzer; private endgültige Zeichenfolge Passwort; Private Final Int Minconnection; Private Final Int MaxConnection; Private Final INT Minidle; privates Final Long Maxwait;
.Connectionpool - Datenbankverbindungspool
Der ConnectionPool -Konstruktor wird als Schutz deklariert, verbietet die externe Erstellung und wird durch ConnectionPoolFactory einheitlich verwaltet.
ConnectionPool implementiert die Methode DataSource-Schnittstelle und Re -GetConnection ().
ConnectionPool enthält zwei Container - eine Warteschlange speichert die Leerlaufverbindung und der andere Vektor (Berücksichtigung der Synchronisation) speichert die verwendete Verbindung.
Wenn der Entwickler eine Datenbankverbindung verwendet, wird sie aus der Warteschlange abgerufen, und wenn es nein gibt, gibt es leer zurück. Wenn die enge Verbindung abgeschlossen ist, wird sie wieder an Vektor gebracht.
ConnectionPool bietet einen einfachen dynamischen Expansionsmechanismus, der auf Minidle und MaxConnection basiert.
private statische endgültige INT initial_size = 5; private statische endgültige Zeichenfolge close_method = "close"; privater statischer Logger -Logger; private intgröße; private ConnectionParam ConnectionParam; Private ArrayBlockingQueue <Connection> idleConnectionqueue; Private Vector <Connection> tousyConnectionVector;
.ConnectionPoolFactory - Verbindungspool -Verwaltungsklasse
ConnectionPoolFactory enthält eine statische Concurrenthashmap, um Verbindungspool -Objekte zu speichern.
Mit ConnectionPoolFactory können Sie mehrere Verbindungspools mit unterschiedlichen Konfigurationen verschiedener Datenbanken erstellen.
Der Entwickler muss den Verbindungspool zum ersten Mal mit einem bestimmten Namen registrieren (binden) und dann die Verbindung jedes Mal aus dem angegebenen Verbindungspool abrufen.
Wenn der Verbindungspool nicht mehr verwendet wird, kann sich der Entwickler den Verbindungspool abmelden (ungebind).
private statische Karte <String, ConnectionPool> poolmap = new ConcurrentHasMap <> (); öffentliche statische Verbindung getConnection (String Poolname) löst SQLEXception {namecheck (Poolname) aus; ConnectionPool ConnectionPool = Poolmap.get (Poolname); return ConnectionPool.getConnection (); } public static void RegisterConnectionPool (String -Name, ConnectionParam ConnectionParam) {RegisterCheck (Name); poolmap.put (Name, neuer ConnectionPool (ConnectionParam)); } // GC public static void unregisterConnectionpool (String name) {namecheck (name); endgültig ConnectionPool ConnectionPool = Poolmap.get (Name); poolmap.remove (name); neuer Thread (new Runnable () {@Override public void run () {ConnectionPool.clear ();}}). start (); }Kerncode
Der Kerncode des Datenbankverbindungspools befindet sich in der Methode getConnection (). Nachdem der Entwickler die Datenbankvorgänge verarbeitet hat, wird die Methode von Close () aufgerufen. Die Verbindung sollte geschlossen und die Ressourcen veröffentlicht werden. Im Datenbankverbindungspool sollte die Verbindung nicht direkt geschlossen werden, sondern wieder in den Pool gelegt und wiederverwendet werden. Hier wird der Java Dynamic Proxy -Mechanismus verwendet. Die GetConnection gibt keine "echte" Verbindung zurück, sondern eine benutzerdefinierte Proxy -Klasse (hier wird anonyme Klasse verwendet). Wenn der Benutzer die Methode close () aufruft, fängt er sie ab und bringt sie wieder in den Pool. Für Dynamic Proxy können Sie sich auf einen anderen Blog "Java Dynamic Proxy Simple Application" beziehen.
@Override public Connection getConnection () löscht sqlexception {try {Final Connection Connection = idleConnectionQueue.poll (ConnectionParam.getMaxwait (), TimeUnit.Milliseconds); if (connection == null) {logger.info (leermsg ()); SEELEALECAPACITY (); null zurückkehren; } tousyConnectionVector.add (Verbindung); return (connection) proxy.newproxyinstance (this.getClass (). getClassloader (), New Class [] {Connection.class}, New InvocationHandler () {@Override public Object Invoke (Object Proxy, Method -Methode, Objekt [] args) löst Throwable {ifable {methods. args); } catch (interruptedException e) {e.printstacktrace (); } return null; }2. Verwendung
Zunächst erstellt der Benutzer die Datenbankverbindungspoolparameter (ConnectionParam), einschließlich Treiber, URL, Benutzer, Kennwort erforderlich. Sie können Optionen wie Minconnection, MaxConnection usw. anpassen. Dies ist der Vorteil der Verwendung von Builder, um eine große Anzahl von Attributen zu erstellen, einschließlich der erforderlichen Attribute und optionalen Attribute. Registrieren Sie den Verbindungspool dann mit dem ConnectionPoolFactory mit einem bestimmten Namen und erhalten Sie schließlich die Verbindung, indem Sie die ConnectionPoolFactory Static Factory -Methode aufrufen.
String driver = "com.mysql.jdbc.driver"; String url = "jdbc: mysql: // localhost: 3306/test"; String user = "root"; String password = "root"; ConnectionParam ConnectionParam = new ConnectionParam.ConnectionParambuilder (Treiber, URL, Benutzer, Passwort) .build (); ConnectionPoolFactory.registerConnectionPool ("Test", ConnectionParam); Connection Connection = ConnectionPoolFactory.getConnection ("Test");3. Code
.ParamConfiguration
Paketdatenbank.Config; Importieren Sie Java.io.Serializable;/** * Datenbankverbindungsparameter * Erstellt von Michael Wong unter 2016/1/18. */public class paramconfiguration implementiert serialisierbar {public static final int min_connection = 5; public static final int max_connection = 50; public static final int min_idle = 5; public static final long max_wait = 30000; private paramconfiguration () {}}.Builder
Paketdatenbank;/** * Builder * Erstellt von Michael Wong unter 2016/1/18. */public interface builder <t> {t Build ();}.ConnectionParam
Paketdatenbank; Datenbank importieren. */public class ConnectionParam {private endgültige String -Treiber; private endgültige String -URL; private endgültige String -Benutzer; private endgültige Zeichenfolge Passwort; Private Final Int Minconnection; Private Final Int MaxConnection; Private Final INT Minidle; privates Final Long Maxwait; private ConnectionParam (ConnectionParambuilder Builder) {this.driver = builder.driver; this.url = builder.url; this.user = builder.user; this.Password = builder.password; this.Minconnection = builder.Minconnection; this.maxConnection = builder.maxConnection; this.minidle = builder.minidle; this.maxwait = builder.maxwait; } public String getDriver () {return this.driver; } public String geturl () {return this.url; } public String getUser () {return this.user; } public String getPassword () {return this.password; } public int getMinconnection () {return this.Minconnection; } public int getmaxConnection () {return this.maxConnection; } public int getminidle () {return this.minidle; } public long getmaxwait () {return this.maxwait; } public static class ConnectionParambuilder implementiert Builder <ConnectionParam> {// Erforderliche Parameter privater String -Treiber; private endgültige String -URL; private endgültige String -Benutzer; private endgültige Zeichenfolge Passwort; // Optionale Parameter - Initialisiert auf Standardwert private int minconnection = paramconfiguration.min_connection; private int maxConnection = paramconfiguration.max_connection; private int minidle = paramconfiguration.min_idle; // Verbindungswartestation privat long maxwait = paramconfiguration.max_wait; public ConnectionParambuilder (String -Treiber, String -URL, String -Benutzer, String -Kennwort) {this.driver = Treiber; this.url = url; this.user = user; this.Password = Passwort; } public ConnectionParambuilder Minconnection (int minconnection) {this.Minconnection = minConnection; gib dies zurück; } public ConnectionParambuilder maxConnection (int maxConnection) {this.maxConnection = maxConnection; gib dies zurück; } public ConnectionParambuilder Minidle (int minidle) {this.minidle = minidle; gib dies zurück; } public ConnectionParambuilder maxwait (int maxwait) {this.maxwait = maxwait; gib dies zurück; } @Override public ConnectionParam build () {return New ConnectionParam (this); }}}.Connectionpool
Paketdatenbank.Factory; Datenbank importieren. java.sql.drivermanager; import Java.sql.sqlexception; import Java.sql.sqlfeaturenotsupportedexception; import Java.util.Vector; Import Java.util.Concurrent.ArrayBlocking; Verbindungspool * Erstellt von Michael Wong am 2016/1/18. */public class ConnectionPool implementiert DataSource {private statische endgültige initial initial_size = 5; private statische endgültige Zeichenfolge close_method = "close"; privater statischer Logger -Logger; private intgröße; private ConnectionParam ConnectionParam; Private ArrayBlockingQueue <Connection> idleConnectionqueue; Private Vector <Connection> tousyConnectionVector; Protected ConnectionPool (ConnectionParam ConnectionParam) {this.connectionParam = ConnectionParam; int maxConnection = ConnectionParam.getMaxConnection (); IDLECONNECTIONQUEUE = New ArrayBlockingQueue <> (maxConnection); belebtConnectionVector = neuer Vektor <> (); logger = logger.getLogger (this.getClass (). getName ()); initConnection (); } private void initConnection () {int minconnection = ConnectionParam.getMinconnection (); init initialSize = initial_size <minconnection? minconnection: initial_size; try {class.forname (ConnectionParam.getDriver ()); für (int i = 0; i <initialSize+ConnectionParam.getMinconnection (); i ++) {idleConnectionQueue.put (newConnection ()); Größe ++; }} catch (Ausnahme e) {neue AusnahmeurinInitializerError (e) werfen; }} @Override public Connection getConnection () löscht sqlexception {try {Final Connection Connection = idleConNectionQueue.poll (ConnectionParam.getMaxwait (), TimeUnit.Milliseconds); if (connection == null) {logger.info (leermsg ()); SEELEALECAPACITY (); null zurückkehren; } tousyConnectionVector.add (Verbindung); return (connection) proxy.newproxyinstance (this.getClass (). getClassloader (), New Class [] {Connection.class}, New InvocationHandler () {@Override public Object Invoke (Object Proxy, Method -Methode, Objekt [] args) löst Throwable {ifable {methods. args); } catch (interruptedException e) {e.printstacktrace (); } return null; } private Verbindung newConnection () löscht sqlexception {String url = ConnectionParam.geturl (); String user = ConnectionParam.getUser (); String password = ConnectionParam.getPassword (); return triverManager.getConnection (URL, Benutzer, Passwort); } protected int size () {return size; } protected int idleConnectionQuantity () {return idleConnectionQueue.size (); } protected int tousyConnectionQuantity () {return tousyConnectionVector.size (); } private void sealecapacity () löst SQLEXception {int minidle = ConnectionParam.getminidle () aus; int maxConnection = ConnectionParam.getMaxConnection (); int newcapacity = Größe + Minidle; Newcapacity = NewCapacity> MaxConnection? MaxConnection: Newcapacity; int GrowCount = 0; if (Größe <newCapacity) {try {for (int i = 0; i <newCapacity - Größe; i ++) {idleConnectionQueue.put (newConnection ()); GrowCount ++; }} catch (interruptedException e) {e.printstacktrace (); }} Größe = Größe + GrowCount; } protected void clear () {try {while (Größe--> 0) {Connection Connection = idleConnectionQueue.take (); Connection.close (); }} catch (InterruptedException | }} private String leermsg () {return "Datenbank ist beschäftigt, bitte warte ..."; } @Override public Connection getConnection (String -Benutzername, String -Passwort) löst SQLEXception {return null; } @Override public Printwriter getLogwriter () löst SQLEXception {return null; } @Override public void setLogwriter (Printwriter out) löst SQLEXception {} @Override public void setLogintimeout (int Sekunden) aus SQLEXception {} @Override public int getLogintimeout () wirft sqlexception {return 0; } @Override public Logger getParentLogger () löst sqlfeaturenotsupportedexception {return null; } @Override public <T> T -Ausweichung (Klasse <T> iface) löst SQLEXception {return null; } @Override public boolean isWrapperfor (class <?> Iface) löst SQLEXception {zurück; }}.ConnectionPoolFactory
Paketdatenbank.Factory; Datenbank importieren. */public class ConnectionPoolFactory {private ConnectionPoolFactory () {} private statische Karte <String, ConnectionPool> poolmap = new ConcurrentHasMap <> (); öffentliche statische Verbindung getConnection (String Poolname) löst SQLEXception {namecheck (Poolname) aus; ConnectionPool ConnectionPool = Poolmap.get (Poolname); return ConnectionPool.getConnection (); } public static void RegisterConnectionPool (String -Name, ConnectionParam ConnectionParam) {RegisterCheck (Name); poolmap.put (Name, neuer ConnectionPool (ConnectionParam)); } // GC public static void unregisterConnectionpool (String name) {namecheck (name); endgültig ConnectionPool ConnectionPool = Poolmap.get (Name); poolmap.remove (name); neuer Thread (new Runnable () {@Override public void run () {ConnectionPool.clear ();}}). start (); } public static int Größe (String Poolname) {namecheck (Poolname); return poolmap.get (Poolname) .Size (); } public static int getIdleConnectionQuantity (String Poolname) {namecheck (Poolname); return poolmap.get (Poolname) .IdleConnectionQuantity (); } public static int getbusyConnectionQuantity (String Poolname) {namecheck (Poolname); return poolmap.get (Poolname) .busyConnectionQuantity (); } private static void RegisterConCheck (String -Name) {if (name == null) {werfen neuer illegalArgumentException (nullname ()); }} private statische void namecheck (String name) {if (name == null) {werfen Sie neue illegalArgumentException (nullname ()); } if (! poolmap.containesKey (name)) {werfen Sie neue illegalArgumentException (namexists (name)); }} private statische String nullname () {return "Poolname darf nicht null sein"; } private statische String -Notizer (String -Name) {return "Verbindungspool mit dem Namen" + Name + "existiert nicht"; }} 4. Test
Junit Unit Tests
Paketdatenbank.Factory; Datenbank importieren 2016/1/20. */public class ConnectionPoolFactoryTest {@Test public void testGetConnection () löst SQLEXception {String driver = "com.mysql.jdbc.driver" aus; String url = "jdbc: mysql: // localhost: 3306/test"; String user = "root"; String password = "root"; ConnectionParam ConnectionParam = new ConnectionParam.ConnectionParambuilder (Treiber, URL, Benutzer, Passwort) .build (); ConnectionPoolFactory.registerConnectionPool ("Test", ConnectionParam); List <Connection> ConnectionList = New ArrayList <> (); für (int i = 0; i <12; i ++) {ConnectionList.add (ConnectionPoolFactory.getConnection ("Test")); } print (); Close (ConnectionList); drucken(); ConnectionList.Clear (); für (int i = 0; i <12; i ++) {ConnectionList.add (ConnectionPoolFactory.getConnection ("Test")); } print (); Close (ConnectionList); ConnectionPoolFactory.unregisterConnectionPool ("Test"); } @Test (erwartet = illegalArgumentException.class) public void testException () {try {ConnectionPoolFactory.getConnection ("Test"); } catch (sqlexception e) {e.printstacktrace (); }} private void close (list <Connection> ConnectionList) löst SQLEXception {for (Connection conn: ConnectionList) {if (conn! = null) {conn.close (); }}} private void print () {System.out.println ("idle:" + connectionPoolFactory.getIdleConnectionQuantity ("Test")); System.out.println ("Beschäftigt:" + ConnectionPoolFactory.getBusyConnectionQuantity ("Test")); System.out.println ("Größe:" + ConnectionPoolFactory.Size ("Test")); }}Das Obige dreht sich alles um diesen Artikel, ich hoffe, es wird für das Lernen aller hilfreich sein.