From source code analysis principle DBCP database connection pool

background

The concept of database connection pool you should be very familiar, similar dbcp, c3p0, there is now commonly used druid, etc., when using nothing more than a few paragraphs copied from the Internet configuration, adjustment to the parameters, but in-depth understanding of the principles of database connection pool in today's Java development is still very necessary here to dbcp example, briefly analyze the realization of the principles of database connection pool

Here I used version is 2.5.0 version

	<dependency>
	    <groupId>org.apache.commons</groupId>
	    <artifactId>commons-dbcp2</artifactId>
	    <version>2.5.0</version>
	</dependency>
复制代码

analysis

ready

Let's look at a native jdbc Code (omitted exception trap):

        Class.forName("com.mysql.cj.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/xxxxxx";

        Connection conn = DriverManager.getConnection(url, "user", "password");
        Statement stat = conn.createStatement();
        ResultSet set = stat.executeQuery(sql);

        while(set.next()) {
            System.out.println(set.getString(2));
        }

        stat.close();
        conn.close();
复制代码

Code structure here do not care about, which is the focus of a

		Connection conn = DriverManager.getConnection(url, "user", "password");
复制代码

Why should I say take this one out alone? Because we use the database connection pool is called getConnection method in the JDBC 2.0 API, Java interfaces to promote the use of DateSource get connected

Our focus now is to find DataSource interface dhcp provided, if we used the dhcp configuration, you must have seen such a configuration

spring:
  datasource:
    type: org.apache.commons.dbcp2.BasicDataSource
复制代码

We have to BasicDataSource as a breakthrough point, this class implements the DataSource interface:

public class BasicDataSource implements DataSource, BasicDataSourceMXBean, MBeanRegistration, AutoCloseable
复制代码

Since we want to explore the principles, do not focus on the minutiae of the section, we point directly into the core of the getConnection method, and in order to clear code structure, where all exceptions capture ignored statements:

    @Override
    public Connection getConnection() throws SQLException {
        if (Utils.IS_SECURITY_ENABLED) {
            final PrivilegedExceptionAction<Connection> action = new PaGetConnection();
            return AccessController.doPrivileged(action);
        }
        return createDataSource().getConnection();
    }
复制代码

The whole code is very simple, safe way according to whether to open the management to choose to get connected, we are here to look no security management

Creating a data source

Let's look at createDataSource method:

    protected DataSource createDataSource() throws SQLException {
        if (closed) {
            throw new SQLException("Data source is closed");
        }
        // 如果数据源已存在,就直接返回
        if (dataSource != null) {
            return dataSource;
        }
        synchronized (this) {
        	// 使用双检锁的设计,保证dataSource是单例的
            if (dataSource != null) {
                return dataSource;
            }

			// 注册MBean,这里不是重点,可以暂时不关心
            jmxRegister();

            // 创建返回原生连接的工厂
            final ConnectionFactory driverConnectionFactory = createConnectionFactory();

            // 创建连接池工厂
            boolean success = false;
            PoolableConnectionFactory poolableConnectionFactory;
            try {
                poolableConnectionFactory = createPoolableConnectionFactory(driverConnectionFactory);
                poolableConnectionFactory.setPoolStatements(poolPreparedStatements);
                poolableConnectionFactory.setMaxOpenPreparedStatements(maxOpenPreparedStatements);
                success = true;
            } catch (final SQLException se) {
                throw se;
            } catch (final RuntimeException rte) {
                throw rte;
            } catch (final Exception ex) {
                throw new SQLException("Error creating connection factory", ex);
            }

            if (success) {
                // 创建连接池成功
                createConnectionPool(poolableConnectionFactory);
            }

            // 创建数据源池来管理连接
            DataSource newDataSource;
            success = false;
            try {
                newDataSource = createDataSourceInstance();
                newDataSource.setLogWriter(logWriter);
                success = true;
            } catch (final SQLException se) {
                throw se;
            } catch (final RuntimeException rte) {
                throw rte;
            } catch (final Exception ex) {
                throw new SQLException("Error creating datasource", ex);
            } finally {
                if (!success) {
                    closeConnectionPool();
                }
            }

            // 如果初始化连接数大于0,就进行预加载
            try {
                for (int i = 0; i < initialSize; i++) {
                    connectionPool.addObject();
                }
            } catch (final Exception e) {
                closeConnectionPool();
                throw new SQLException("Error preloading the connection pool", e);
            }

            // 如果空间连接回收器执行间隔时间大于0,则添加回收器任务
            startPoolMaintenance();

			// 返回数据源
            dataSource = newDataSource;
            return dataSource;
        }
    }
复制代码

Code appears to be very long, in fact, the process is simple:

  1. Analyzing the results can be directly returned (closed or data source already exists), and if so, return directly
  2. Returned to the factory to create a database connection
  3. Create a connection pool factory
  4. With the connection factory database to the packaging factory connection pool
  5. Create a database connection pool
  6. To create a data source instance through a connection pool
  7. The parameter settings to determine whether to perform additional operations (such as pre-loaded database connectivity)
  8. Data source return

We focus on only three: the database connection factory what is and what connection pool factory, what is the connection pool yes. Let's look at the database connection factory

createConnectionFactory

The code for this method as compared to the above and some confusion, in order to facilitate understanding, here I still ignore all the exceptions capture code segment:

    protected ConnectionFactory createConnectionFactory() throws SQLException {
        // 加载JDBC驱动
        Driver driverToUse = this.driver;

        if (driverToUse == null) {
            Class<?> driverFromCCL = null;
            if (driverClassName != null) {
                if (driverClassLoader == null) {
                    driverFromCCL = Class.forName(driverClassName);
                } else {
                    driverFromCCL = Class.forName(driverClassName, true, driverClassLoader);
                }
            }

            if (driverFromCCL == null) {
            	// 这里的url就是我们与数据库建立连接的url
                driverToUse = DriverManager.getDriver(url);
            } else {
                driverToUse = (Driver) driverFromCCL.getConstructor().newInstance();
                if (!driverToUse.acceptsURL(url)) {
                    throw new SQLException("No suitable driver", "08001");
                }
            }
        }

        // 设置连接
        final String user = userName;
        if (user != null) {
            connectionProperties.put("user", user);
        } else {
            log("DBCP DataSource configured without a 'username'");
        }

        final String pwd = password;
        if (pwd != null) {
            connectionProperties.put("password", pwd);
        } else {
            log("DBCP DataSource configured without a 'password'");
        }

        final ConnectionFactory driverConnectionFactory = new DriverConnectionFactory(driverToUse, url,
                connectionProperties);
        return driverConnectionFactory;
    }
复制代码

This method is very simple to do, is to simply load the driver, creating a connection factory is really the penultimate line of new DriverConnectionFactory this code, but also DriverConnectionFactory final type of object we created, where we pass the database drive to the factory object connection url, user name, and password

About DriverConnectionFactory factory class, some of them listed here code, I do not believe the other to speak more

    @Override
    public Connection createConnection() throws SQLException {
        return driver.connect(connectionString, properties);
    }
复制代码
PoolableConnectionFactory

Then look at the connection pool factory class, but we are not prepared to study this class, but returned to createDataSource method, found a createConnectionPool method, passing in the PoolableConnectionFactory class object, we enter this process:

    protected void createConnectionPool(final PoolableConnectionFactory factory) {
        // 创建一个对象池
        final GenericObjectPoolConfig<PoolableConnection> config = new GenericObjectPoolConfig<>();
        
        updateJmxName(config);
        config.setJmxEnabled(registeredJmxObjectName != null);
        
        final GenericObjectPool<PoolableConnection> gop = createObjectPool(factory, config, abandonedConfig);
        gop.setMaxTotal(maxTotal);
        gop.setMaxIdle(maxIdle);
        gop.setMinIdle(minIdle);
        gop.setMaxWaitMillis(maxWaitMillis);
        gop.setTestOnCreate(testOnCreate);
        gop.setTestOnBorrow(testOnBorrow);
        gop.setTestOnReturn(testOnReturn);
        gop.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
        gop.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        gop.setSoftMinEvictableIdleTimeMillis(softMinEvictableIdleTimeMillis);
        gop.setTestWhileIdle(testWhileIdle);
        gop.setLifo(lifo);
        gop.setSwallowedExceptionListener(new SwallowedExceptionLogger(log, logExpiredConnections));
        gop.setEvictionPolicyClassName(evictionPolicyClassName);
        
        factory.setPool(gop);
        connectionPool = gop;
    }
复制代码
  1. The first line creates an object pool , Apache provides four object pooling, GenericObjectPoolConfig just one, if we do not care about the details of its implementation In this, just need to understand that it is a object pool is enough
  2. The next two lines and related Jmx here for the time being do not care
  3. Then the number of rows to create an object pool by connecting pools and other configuration we offer, and set variable values ​​that can be set by our configuration file
  4. Finally, we then provided to the connection pool PoolableConnectionFactory factory class, and the data source itself is set to the connection pool connection pool

Now understand why we do not directly studied PoolableConnectionFactory it, because in fact we call the connection pool is connectionPool

Get connected

We have just introduced the connection factory, connection pooling and connection pool factory, in order to prevent you forget what to do next, and then we began to study this code snippet impress:

		return createDataSource().getConnection();
复制代码

Was just studied createDataSource method, which ultimately returns a data source object (the data source object through connectionPool package comes PoolingDataSource <PoolableConnection> objects), we are going to get really depends on the method of connection getConnection

This is an abstract method, because createDataSource returns PoolingDataSource object, so we object to this analysis, in order to make the code structure clear, there is still an exception omitted capture the code:

    @Override
    public Connection getConnection() throws SQLException {
        final C conn = pool.borrowObject();
        if (conn == null) {
            return null;
        }
        return new PoolGuardConnectionWrapper<>(conn);
    }
复制代码

We may have to address two questions:

  • Connection C is a subclass of
  • PoolGuardConnectionWrapper to ensure that the packaging is closed type connector connection can not be reused

Now it should be no problem, the whole process is to get connected, get less than if it returns a null, otherwise it returns a wrapper class. Connection to get through borrowObject way to achieve this approach is in GenericObjectPool in (do not know why it must be in front of the code for this class is not a good look), about the implementation of this class is not Benpian focus, in the future I will alone write an article to introduce four kinds of objects provided by the Apache pool

to sum up

Finally accordance with established practice, to sum up DBCP connection pool acquiring step connection getConnection method to invoke here to get a connection example to analyze:

  1. Analyzing the data source is off or already exists, if an exception is thrown off, if it exists directly returned
  2. Create a factory class can return the database connection, and load the database driver
  3. The connection factory class through the database to create a connection pool factory class
  4. By connecting pool factory class, create a database connection pool and set properties
  5. To create a data source through a connection pool, and some initialization operations according to configurable properties
  6. To get the object by the data source object pool (connector)

Reproduced in: https: //juejin.im/post/5cf0756c6fb9a07f04202e32

Guess you like

Origin blog.csdn.net/weixin_34357267/article/details/91477689