1. Introdução
A tecnologia de agrupamento é amplamente utilizada em Java. Em resumo, ele usa um pool de objetos para armazenar uma instância com número limitado de instâncias. Os desenvolvedores obtêm instâncias do pool de objetos e depois voltam para o pool de objetos após o uso, reduzindo assim a sobrecarga da criação frequente de objetos do sistema e destruindo objetos até certo ponto. O pool de threads Java e o conjunto de conexões de banco de dados são aplicativos típicos, mas nem todos os objetos são adequados para o pool. O agrupamento de objetos com sobrecarga relativamente pequena afetará o desempenho, porque a manutenção de pools de objetos também requer uma certa sobrecarga de recursos. Para objetos com alta criação e uso frequentes e frequentes, o uso da tecnologia de pool melhorará bastante o desempenho.
Existem muitos pools de conexão de banco de dados maduros no setor, como Druid C3P0, DBCP, Proxool e Alibaba. Existem muitos e de código aberto, e você pode encontrar o código -fonte no GitHub. Os desenvolvedores podem escolher com base em suas próprias necessidades e em suas características e desempenho. Este artigo é apenas para entender a tecnologia de pool de aprendizado e implementar um pool de conexão de banco de dados simples. Se houver algum erro, espero criticá -los e corrigi -los.
2. Design
Principais classes e interfaces
.ConnectionParam - Classe de parâmetros do pool de conexão do banco de dados, responsável por configurar conexões de banco de dados e parâmetros relacionados ao pool de conexões. Implementação usando o construtor.
Driver URL Usuário Senha - necessária para conectar -se ao banco de dados
Minconnection - Número mínimo de conexões
MaxConnection - Número máximo de conexões
Minidle - Número mínimo de conexões ociosas
Maxwait - Tempo máximo de espera
driver de string final privado; URL de string final privado; Usuário de String final privado; senha de string final privada; Minconnection privado final Int; private final int maxConnection; PRIVADO FINAL INT MINIDLE; Máxwait final final privado;
.ConnectionPool - Pool de conexão de banco de dados
O construtor do ConnectionPool é declarado como proteção, proíbe a criação externa e é gerenciado uniformemente pelo ConnectionPoolFactory.
O ConnectionPool implementa o método da interface do DataSource e re-getConnection ().
O ConnectionPool contém dois recipientes - uma fila armazena a conexão ociosa e o outro vetor (considerando a sincronização) armazena a conexão em uso.
Quando o desenvolvedor usa uma conexão de banco de dados, ele é recuperado da fila e, se não houver, retorna vazio; Quando a conexão estreita é concluída, ela é devolvida ao vetor.
O ConnectionPool fornece um mecanismo de expansão dinâmica simples baseado no minidle e no maxConnection.
private estático final int inicial_size = 5; String final estática privada Close_method = "Close"; Logger estático privado; Tamanho privado int; ConnectionParam de conexão privadoParam; Private ArrayBlockingQueue <neconned> IdleConnectionQueue; vetor privado <neconnect> BusyConnectionVector;
.ConnectionPoolFactory - Connection Pool Gerenciamento da classe
O ConnectionPoolFactory mantém um concorrente estático para armazenar objetos de pool de conexão.
O ConnectionPoolFactory permite a criação de vários pools de conexão com diferentes configurações de diferentes bancos de dados.
O desenvolvedor precisa registrar (vincular) o pool de conexão com um nome específico pela primeira vez e, em seguida, obter a conexão do pool de conexão especificado a cada vez.
Se o pool de conexões não estiver mais em uso, o desenvolvedor poderá fazer logon (desbaste) o pool de conexões.
mapa estático privado <string, ConnectionPool> PoolMap = novo ConcurrentHashMap <> (); A conexão estática pública getConnection (String poolname) lança SqLexception {NameCheck (PoolName); ConnectionPool ConnectionPool = PoolMap.get (PoolName); return ConnectionPool.getConnection (); } public static void RegisterConnectionPool (Nome da String, ConnectionParam ConnectionParam) {RegisterCheck (nome); Poolmap.put (nome, new ConnectionPool (ConnectionParam)); } // Deixe o gc Final ConnectionPool ConnectionPool = PoolMap.get (nome); Poolmap.remove (nome); novo thread (novo runnable () {@Override public void run () {ConnectionPool.clear ();}}). start (); }Código central
O código principal do pool de conexão do banco de dados é o método getConnection (). Geralmente, depois que o desenvolvedor processou as operações do banco de dados, o método Close () será chamado. A conexão deve ser fechada e os recursos devem ser divulgados. No pool de conexão do banco de dados, quando o usuário chama o método Close (), a conexão não deve ser fechada diretamente, mas deve ser colocada de volta na piscina e reutilizada. Aqui, o mecanismo de proxy dinâmico Java é usado. A getConnection retorna não uma conexão "real", mas uma classe de proxy personalizada (a classe anônima é usada aqui). Quando o usuário chama o método Close (), ele intercepta e o coloca de volta no pool. Para proxy dinâmico, você pode se referir a outro blog "Aplicativo simples de proxy dinâmico Java"
@Override public Connection getConnection () lança sqLexception {try {Final Connection Connection = idleconConCenectionQueue.poll (ConnectionParam.getMaxwait (), timeUnit.millisEconds); if (conexão == null) {Logger.info (emppymsg ()); segurecapacity (); retornar nulo; } BusyConnectionVector.add (conexão); return (conexão) proxy.newProxyInstance (this.getclass (). getClassLoader (), nova classe [] {Connection.class}, new InvocationHandler () {@Override public Object Invoke (Object Proxy, Método, Método, Object [] args) Throwsable {if (! args); } catch (interruptedException e) {e.printStackTrace (); } retornar nulo; }2. Use
Primeiro, o usuário constrói os parâmetros do pool de conexão com o banco de dados (ConnectionParam), incluindo driver, URL, usuário, itens necessários de senha. Você pode personalizar opções como Minconnection, MaxConnection, etc. Se não estiver definido, use o valor padrão do sistema. Este é o benefício do uso do construtor para criar um grande número de atributos, incluindo atributos necessários e atributos opcionais. Em seguida, registre o pool de conexão com o ConnectionPoolFactory com um nome específico e, finalmente, obtenha a conexão chamando o método de fábrica estática do ConnectionPoolFactory.
String driver = "com.mysql.jdbc.driver"; String url = "jdbc: mysql: // localhost: 3306/teste"; String user = "root"; String senha = "root"; ConnectionParam ConnectionParam = new ConnectionParam.ConnectionParambuilder (Driver, URL, Usuário, Senha) .build (); ConnectionPoolFactory.RegisterConnectionPool ("Test", ConnectionParam); Conexão de conexão = conexãoPoolFactory.getConnection ("teste");3. Código
.Paramconfiguration
pacote database.config; importar java.io.serializable;/** * Parâmetros de conexão do banco de dados * criados por Michael Wong em 2016/1/18. */classe pública paramconfiguration implementa serializável {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; Paramconfiguration privado () {}}.Construtor
banco de dados de pacotes;/** * Builder * Criado por Michael Wong em 2016/1/18. */Builder de interface pública <T> {t build ();}.ConnectionParam
Database do pacote; importar database.config.paramconfiguration;/** * Parâmetros de conexão do banco de dados * criados por Michael Wong em 2016/18. */public class ConnectionParam {private final String Driver; URL de string final privado; Usuário de String final privado; senha de string final privada; Minconnection privado final Int; private final int maxConnection; PRIVADO FINAL INT MINIDLE; Máxwait final final privado; ConnectionParam privado (conexão parambuilder Builder) {this.driver = builder.driver; this.url = builder.url; this.User = Builder.User; this.password = builder.password; this.minconnection = construtor.minconnection; this.MaxConnection = Builder.MaxConnection; this.Minidle = Builder.Minidle; this.maxwait = construtor.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; } classe estática pública ConnectionParambuilder implementa o construtor <neconnecTaram> {// Parâmetros necessários Driver de sequência final privado; URL de string final privado; Usuário de String final privado; senha de string final privada; // parâmetros opcionais - inicializado para o valor padrão private int mInconnection = paramconfiguration.min_connection; private int maxConnection = paramconfiguration.max_connection; private int minidle = paramconfiguration.min_idle; // obtendo o tempo de espera de conexão privado maxwait = paramconfiguration.max_wait; public ConnectionParambuilder (driver da string, URL da string, usuário da string, senha da string) {this.driver = driver; this.url = url; this.User = usuário; this.password = senha; } Public ConnectionParambuilder MENCONNENCION (INT MINCONNENCION) {this.minconnection = MENCONNENCIONO; devolver isso; } public ConnectionParambuilder maxConnection (int maxConnection) {this.maxConnection = maxConnection; devolver isso; } public ConnectionParambuilder Minidle (int minidle) {this.minidle = minidle; devolver isso; } public ConnectionParambuilder Maxwait (int maxwait) {this.maxwait = maxwait; devolver isso; } @Override public ConnectionParam build () {return new ConnectionParam (this); }}}.ConnectionPool
banco de dados do pacote.factory; importar database.connectionParam; importar javax.sql.datasource; importar java.io.printwriter; importar java.lang.reflect.invocationHandler; import java.lang.reflect.Method; java.sql.driverManager; importar java.sql.sqLexception; importar java.sql.sqlFeatureNotSupportEdException; importar java.util.vector; importlaving.util.Concurrent.arrayBlockingUe; import jovAtil.Concurntent.TimeTynTern.TimeRent.arrayBlockingUe; import joGil.Concurntent.TimeRilit.OnTern.Ternit.Ternit.Ternit.Ternit.Ternit.Ternit.Ternit.Ternit.TernTerT.OnGaRIliT.OnTeril.Ternit.TimeRilit.OnGaRilit.OnGaRilernTern.TimeRilit.OnTerT.Time; * Pool de conexão * Criado por Michael Wong em 2016/18. */public class ConnectionPool implementa DataSource {private estático final int inicial_size = 5; String final estática privada Close_method = "Close"; Logger estático privado; Tamanho privado int; ConnectionParam de conexão privadoParam; Private ArrayBlockingQueue <neconned> IdleConnectionQueue; vetor privado <neconnect> BusyConnectionVector; ConnectionPool protegido (ConnectionParam ConnectionParam) {this.ConnectionParam = ConnectionParam; int maxConnection = ConnectionParam.getMaxConnection (); iDleConnectionQueue = novo ArrayBlockingQueue <> (maxConnection); BusyConnectionVector = novo vetor <> (); logger = logger.getLogger (this.getClass (). getName ()); initConnection (); } private void initConnection () {int miConnection = ConnectionParam.getMinconnection (); int inicialSize = Initial_size <Minconnection? Minconnection: Initial_size; tente {Class.ForName (ConnectionParam.getDriver ()); for (int i = 0; i <InitialSize+ConnectionParam.getMinconnection (); i ++) {IdleconnectionQueue.put (newConnection ()); tamanho ++; }} catch (Exceção e) {lança nova excepçãoinInitializerError (e); }} @Override Public Connection getConnection () lança sqLexception {try {final conexão de conexão = idleConnectionQueue.poll (ConnectionParam.getMaxwait (), timeUnit.millisEconds); if (conexão == null) {Logger.info (emppymsg ()); segurecapacity (); retornar nulo; } BusyConnectionVector.add (conexão); return (conexão) proxy.newProxyInstance (this.getclass (). getClassLoader (), nova classe [] {Connection.class}, new InvocationHandler () {@Override public Object Invoke (Object Proxy, Método, Método, Object [] args) Throwsable {if (! args); } catch (interruptedException e) {e.printStackTrace (); } retornar nulo; } conexão privada newConnection () lança SqLexception {String url = ConnectionParam.geturl (); String user = ConnectionParam.getUser (); String senha = ConnectionParam.getPassword (); retornar driverManager.getConnection (URL, usuário, senha); } protegido Int size () {return size; } protegido int idleConnectionQuantity () {return iDleConnectionQueue.size (); } protegido int busyConnectionQuantity () {return busyConnectionVector.size (); } private void secrUcApacity () lança sqLexception {int minidle = ConnectionParam.getminidle (); int maxConnection = ConnectionParam.getMaxConnection (); int newCapacity = size + minidle; newCapacity = newCapacity> maxConnection? MaxConnection: NewCapacity; int GrowCount = 0; if (size <newCapacity) {try {for (int i = 0; i <newCapacity - size; i ++) {idleConnectionQueue.put (newConnection ()); GrowCount ++; }} catch (interruptEdException e) {e.printStackTrace (); }} size = size + GrowCount; } Protected void clear () {try {while (size--> 0) {Connection Connection = IdleConnectionQueue.take (); conexão.Close (); }} catch (interruptEdException | sqLexception e) {e.printStackTrace (); }} private string emappymsg () {return "banco de dados está ocupado, por favor, espere ..."; } @Override Public Connection GetConnection (String UserName, String senha) lança SQLEXCECCIONE {return null; } @Override public printWriter getLogWriter () lança sqlexception {return null; } @Override public void SetLogWriter (PrintWriter Out) lança SQLEXCECCETION {} @Override public void setLogIntimeout (int segundos) lança sqLexception {} @Override public int getLogIntimeout () lança sqlexception {return 0; } @Override public Logger getParentLogger () lança sqlfeatureNotSupportEdException {return null; } @Override Public <T> t Under (classe <T> iface) lança o sqLexception {return null; } @Override public boolean iswrapperfor (classe <?> Iface) lança sqlexception {return false; }}.ConnectionPoolFactory
Database de pacote.Factory; importar database.ConnectionParam; importar java.sql.connection; importar java.sql.sqLexception; importar java.util.map; importar java.util.concurrent.concurrenthashmap;/** ** ** Pool de conexão * criado por Michael. */public class ConnectionPoolFactory {private ConnectionPoolFactory () {} mapa estático privado <String, ConnectionPool> PoolMap = novo ConcurrentHashMap <> (); A conexão estática pública getConnection (String poolname) lança SqLexception {NameCheck (PoolName); ConnectionPool ConnectionPool = PoolMap.get (PoolName); return ConnectionPool.getConnection (); } public static void RegisterConnectionPool (Nome da String, ConnectionParam ConnectionParam) {RegisterCheck (nome); Poolmap.put (nome, new ConnectionPool (ConnectionParam)); } // Deixe o gc Final ConnectionPool ConnectionPool = PoolMap.get (nome); Poolmap.remove (nome); novo thread (novo runnable () {@Override public void run () {ConnectionPool.clear ();}}). start (); } public static int size (string poolname) {nameCheck (piscusname); Retornar PoolMap.get (PoolName) .size (); } public static int getidleConnectionQuantity (string poolname) {namecheck (piscina); Retornar PoolMap.get (PoolName) .idleConnectionQuantity (); } public static int getBusyConnectionQuantity (string poolname) {nameCheck (piscina); Retornar PoolMap.get (PoolName) .busyConnectionQuantity (); } private estático void RegisterCheck (nome da string) {if (name == null) {lança novo ilegalArgumentException (nullName ()); }} private estático void nameCheck (nome da string) {if (name == null) {lança novo ilegalargumentException (nullName ()); } if (! Poolmap.containsKey (nome)) {lança nova ilegalArgumentException (notexists (nome)); }} string estática privada nullName () {return "Nome do pool não deve ser nulo"; } string estática privada notaxists (nome da string) {return "Connection pool chamado" + nome + "não existe"; }} 4. Teste
Teste de unidade JUNIT
banco de dados de pacotes.Factory; importar banco de dados.ConnectionParam; importar org.junit.test; importar java.sql.connection; importar java.sql.sqLexception; importar java.util.ArrayList; importar java.ut.list; importação ou staaty odilit.ArrayList; 2016/10/20. */public class ConnectionPoolFactoryTest {@Test public void testGetConnection () lança sqlexception {string driver = "com.mysql.jdbc.driver"; String url = "jdbc: mysql: // localhost: 3306/teste"; String user = "root"; String senha = "root"; ConnectionParam ConnectionParam = new ConnectionParam.ConnectionParambuilder (Driver, URL, Usuário, Senha) .build (); ConnectionPoolFactory.RegisterConnectionPool ("Test", ConnectionParam); List <neconned> ConnectionList = new ArrayList <> (); for (int i = 0; i <12; i ++) {ConnectionList.add (ConnectionPoolFactory.getConnection ("test")); } imprimir(); fechar (ConnectionList); imprimir(); ConnectionList.clear (); for (int i = 0; i <12; i ++) {ConnectionList.add (ConnectionPoolFactory.getConnection ("test")); } imprimir(); fechar (ConnectionList); ConnectionPoolFactory.unregisterConnectionPool ("Test"); } @Test (esperado = ilegalargumentException.class) public void testException () {try {ConnectionPoolFactory.getConnection ("test"); } catch (sqLexception e) {e.printStackTrace (); }} private void Close (list <neconnectListlist) lança sqlexception {for (conexão conn: conexãolist) {if (conn! = null) {conn.close (); }}} private void print () {System.out.println ("Idle:" + ConnectionPoolFactory.getIdleConnectionQuantity ("test"); System.out.println ("Ocupado:" + ConnectionPoolFactory.getBusyConnectionQuantity ("Test")); System.out.println ("size:" + ConnectionPoolFactory.size ("test")); }}O exposto acima é tudo sobre este artigo, espero que seja útil para o aprendizado de todos.