连接池对连接复用的原理实践

一:这里就通过数据库连接池来实践,像Jedis池等原理都是差不多的;

二:代码

1.MyConnection类代码

package me.silentdoer.connectionpool.pool;

import java.sql.*;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * @author silentdoer
 * @version 1.0
 * @description the description
 * @date 4/30/18 3:37 PM
 */
public class MyConnection implements Connection{
    private int connIdentify;
    private Connection conn;
    private boolean closed = false;
    // 如果不是通过MyDataPool创建的此对象则此属性为null;
    private MyPoolDataSource dataSource;  // 重要

    private void checkState(){
        if(this.closed){
            throw new IllegalStateException("Connection已经close");
        }
    }

    @Override
    public Statement createStatement() throws SQLException {
        this.checkState();
        return this.conn.createStatement();
    }

    @Override
    public PreparedStatement prepareStatement(String s) throws SQLException {
        this.checkState();
        return this.conn.prepareStatement(s);
    }

    @Override
    public CallableStatement prepareCall(String s) throws SQLException {
        this.checkState();
        return this.prepareCall(s);
    }

    @Override
    public String nativeSQL(String s) throws SQLException {
        return null;
    }

    @Override
    public void setAutoCommit(boolean b) throws SQLException {
        this.checkState();
        this.conn.setAutoCommit(b);
    }

    @Override
    public boolean getAutoCommit() throws SQLException {
        this.checkState();
        return this.conn.getAutoCommit();
    }

    @Override
    public void commit() throws SQLException {
        this.checkState();
        this.conn.commit();
    }

    @Override
    public void rollback() throws SQLException {
        this.checkState();
        this.conn.rollback();
    }

    @Override
    public void close() throws SQLException {
        this.checkState();
        if(this.dataSource != null){
            //System.out.println("UUUUUUUUUUUU");
            this.dataSource.freeConnection(this);
        }else{
            this.conn.close();
        }
        this.conn = null;
        this.closed = true;
    }

    @Override
    public boolean isClosed() throws SQLException {
        return this.closed;
    }

    @Override
    public DatabaseMetaData getMetaData() throws SQLException {
        this.checkState();
        return this.conn.getMetaData();
    }

    // TODO 只能select ??
    @Override
    public void setReadOnly(boolean b) throws SQLException {
        this.checkState();
        this.conn.setReadOnly(b);
    }

    @Override
    public boolean isReadOnly() throws SQLException {
        this.checkState();
        return this.conn.isReadOnly();
    }

    // TODO 后面就不写了

    @Override
    public void setCatalog(String s) throws SQLException {

    }

    @Override
    public String getCatalog() throws SQLException {
        return null;
    }

    @Override
    public void setTransactionIsolation(int i) throws SQLException {

    }

    @Override
    public int getTransactionIsolation() throws SQLException {
        return 0;
    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
        return null;
    }

    @Override
    public void clearWarnings() throws SQLException {

    }

    @Override
    public Statement createStatement(int i, int i1) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String s, int i, int i1) throws SQLException {
        return null;
    }

    @Override
    public CallableStatement prepareCall(String s, int i, int i1) throws SQLException {
        return null;
    }

    @Override
    public Map<String, Class<?>> getTypeMap() throws SQLException {
        return null;
    }

    @Override
    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {

    }

    @Override
    public void setHoldability(int i) throws SQLException {

    }

    @Override
    public int getHoldability() throws SQLException {
        return 0;
    }

    @Override
    public Savepoint setSavepoint() throws SQLException {
        return null;
    }

    @Override
    public Savepoint setSavepoint(String s) throws SQLException {
        return null;
    }

    @Override
    public void rollback(Savepoint savepoint) throws SQLException {

    }

    @Override
    public void releaseSavepoint(Savepoint savepoint) throws SQLException {

    }

    @Override
    public Statement createStatement(int i, int i1, int i2) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String s, int i, int i1, int i2) throws SQLException {
        return null;
    }

    @Override
    public CallableStatement prepareCall(String s, int i, int i1, int i2) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String s, int i) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String s, int[] ints) throws SQLException {
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String s, String[] strings) throws SQLException {
        return null;
    }

    @Override
    public Clob createClob() throws SQLException {
        return null;
    }

    @Override
    public Blob createBlob() throws SQLException {
        return null;
    }

    @Override
    public NClob createNClob() throws SQLException {
        return null;
    }

    @Override
    public SQLXML createSQLXML() throws SQLException {
        return null;
    }

    @Override
    public boolean isValid(int i) throws SQLException {
        return false;
    }

    @Override
    public void setClientInfo(String s, String s1) throws SQLClientInfoException {

    }

    @Override
    public void setClientInfo(Properties properties) throws SQLClientInfoException {

    }

    @Override
    public String getClientInfo(String s) throws SQLException {
        return null;
    }

    @Override
    public Properties getClientInfo() throws SQLException {
        return null;
    }

    @Override
    public Array createArrayOf(String s, Object[] objects) throws SQLException {
        return null;
    }

    @Override
    public Struct createStruct(String s, Object[] objects) throws SQLException {
        return null;
    }

    @Override
    public void setSchema(String s) throws SQLException {

    }

    @Override
    public String getSchema() throws SQLException {
        return null;
    }

    @Override
    public void abort(Executor executor) throws SQLException {
        this.conn.abort(executor);
    }

    @Override
    public void setNetworkTimeout(Executor executor, int i) throws SQLException {
        this.conn.setNetworkTimeout(executor, i);
    }

    @Override
    public int getNetworkTimeout() throws SQLException {
        return this.conn.getNetworkTimeout();
    }

    @Override
    public <T> T unwrap(Class<T> aClass) throws SQLException {
        return this.conn.unwrap(aClass);
    }

    @Override
    public boolean isWrapperFor(Class<?> aClass) throws SQLException {
        return this.conn.isWrapperFor(aClass);
    }

    // TODO 只允许同一个包里的类进行getter
    Connection getConn() {
        return conn;
    }

    // TODO 只允许同一个包里的类进行setter
    void setConn(Connection conn) {
        this.conn = conn;
    }

    void setDataSource(MyPoolDataSource dataSource) {
        this.dataSource = dataSource;
    }

    public int getIdentify() {
        return connIdentify;
    }

    void setIdentify(int connIdentify) {
        this.connIdentify = connIdentify;
    }
}

这里通过外观模式将MyConnection设计为Connection对象的外观,当close时会通过dataSource来释放对conn的占用以便共其它地方复用,注意close时要将closed设置为true和conn = null;防止当前上下文关闭后却重用的情况发生;

2.MyPoolDataSource类

package me.silentdoer.connectionpool.pool;

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * @author silentdoer
 * @version 1.0
 * @description 这里主要是实现数据库连接池原理,就不实现DataSource接口了,一些校验也会省略;
 * @date 4/30/18 4:36 PM
 */
public class MyPoolDataSource {
    private String driver;
    private String url;
    private String username;
    private String password;
    private boolean autoCommit = true;
    private boolean initialized = false;
    // TODO 只是用来产生数据源(即Connection对象)
    private MyDataSource dataSource = new MyDataSource();
    // TODO 正在被客户使用的Connection对象
    Queue<Connection> usagingConnections = new LinkedList<>();  // 这里最好用并发包
    // TODO 用户释放后还没有被使用的Connection对象
    Queue<Connection> idleConnections = new LinkedList<>();
    // TODO free的Connection对象,通过一个消费者线程来处理
    //Queue<Connection> releasedConnections = new LinkedList<>();

    private void init(){
        try {
            Class.forName(this.driver);
        }catch (Exception ex){}
        this.initialized = true;
    }

    public Connection getConnection(){
        //Connection connection = this.dataSource.getConnection();
        Connection connection = null;
        synchronized (this.idleConnections) {  // 如果多个地方都用到了这块方法则可以提取为一个小模块并做并发数据安全处理
            if (this.idleConnections.size() > 0) {
                // TODO 这里其实还可以根据策略来获取,比如空闲时间最长的优先获取或最后获取等等
                connection = this.idleConnections.poll();
            }else{
                connection = this.dataSource.getConnection();
            }
            synchronized (this.usagingConnections) {
                usagingConnections.offer(connection);
            }
        }
        connection = this.wrapConnection(connection);
        try {
            connection.setAutoCommit(this.autoCommit);
        }catch (Exception ex){}
        return connection;
    }

    private Connection wrapConnection(Connection conn){
        MyConnection connection = new MyConnection();
        connection.setConn(conn);
        connection.setDataSource(this);
        connection.setIdentify(conn.hashCode());
        return connection;
    }

    // 不考虑异常情况
    private Connection unwrapConnection(Connection connection){
        return ((MyConnection)connection).getConn();
    }

    // TODO 此种情况下包访问权限就很有用了
    void freeConnection(Connection connection){
        if(connection == null){
            /*if(logger.isDebugEnable()){
                logger.debug("释放了null connection");
            }*/
        }
        // 这里就不实现生产者消费者模型了,可以参考我的日志系统的实现方式来实现通过消费者线程来获取并处理releasedConnections的connection对象
        connection = this.unwrapConnection(connection);
        synchronized (this.usagingConnections){
            this.usagingConnections.remove(connection);
            synchronized (this.idleConnections){
                this.idleConnections.offer(connection);
            }
            // TODO 这里其实还应该清理一下connection的如statement缓存之类的,暂不知道api是什么,或者其实不需要?
        }
    }

    private class MyDataSource {
        // 这是一种设置是否需要输出框架内部debug信息的方式,但是最好的还是通过用JDK自带的或commons-logging里配置特殊的logger,然后设置这个logger的异常level
        // 来实现是否输出异常,logger的level不够那么判断logger.isDebugEnabled()自然不会成立(需要主动配置logger在文件里否则自动的logger level不够)
        private boolean DEBUG;

        Connection getConnection(){
            Connection conn = null;
            if(!MyPoolDataSource.this.initialized){
                init();
            }
            try {
                conn = DriverManager.getConnection(MyPoolDataSource.this.url, username, password);
            }catch (Exception ex){
                return null;
            }
            return conn;
        }
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setAutoCommit(boolean autoCommit) {
        this.autoCommit = autoCommit;
    }
}

这里通过一个真正的DataSource来制造标准的Connection对象,然后外层会将这个对象作为MyConnection的通信组件进行装配,并且进行缓存等操作,最终才将Connection的外观类MyConnection对象返回给客户端;

3.测试用例

package me.silentdoer.connectionpool;

import com.mysql.jdbc.MySQLConnection;
import me.silentdoer.connectionpool.pool.MyConnection;
import me.silentdoer.connectionpool.pool.MyPoolDataSource;

import java.sql.*;

/**
 * @author silentdoer
 * @version 1.0
 * @description the description
 * @date 4/30/18 3:37 PM
 */
public class Entrance {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        MyPoolDataSource dataSource = new MyPoolDataSource();
        dataSource.setDriver("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/db_test");
        dataSource.setUsername("test");
        dataSource.setPassword("test");
        dataSource.setAutoCommit(true);
        /**-------------------------------------------------------*/
        Connection connection = dataSource.getConnection();
        System.out.println(String.format("第一次获得connection的Identify为:%s", ((MyConnection)connection).getIdentify()));
        PreparedStatement statement = connection.prepareStatement("select uid, name, gender from student where uid = ?");
        statement.setObject(1, 1);
        statement.execute();
        ResultSet resultSet = statement.getResultSet();
        ResultSetMetaData metaData = resultSet.getMetaData();
        int clmCount = metaData.getColumnCount();
        while(resultSet.next()){
            for(int i=1;i<=clmCount;i++){
                System.out.print(resultSet.getObject(i) + "    ");
            }
            System.out.println();
        }
        Connection connection2 = dataSource.getConnection();
        // 可以看到第一次为610998173,这次为1051754451
        System.out.println(String.format("第二次(未释放第一次)获得connection的Identify为:%s", ((MyConnection)connection2).getIdentify()));
        resultSet.close();
        statement.close();
        // 释放第一次的connection
        connection.close();
        Connection connection3 = dataSource.getConnection();
        // 这一次是610998173和已经释放的connection一样,说明完成了connection组件的复用
        System.out.println(String.format("第三次(已释放第一次)获得connection的Identify为:%s", ((MyConnection)connection3).getIdentify()));
    }
}
原文地址:https://www.cnblogs.com/silentdoer/p/8974244.html