1. 程式人生 > >04.手寫資料庫連線池

04.手寫資料庫連線池

1. 設計思路

  • 在內部物件池中,維護一定數量的資料庫連線,並對外暴露資料庫連線的獲取和返回方法。

  • 如外部使用者可通過 getConnection 方法獲取資料庫連線,使用完畢後再通過 releaseConnection 方法將連線返回,注意此時的連線並沒有關閉,而是由連線池管理器回收,併為下一次使用做好準備。

2. pom.xml

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>
mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>RELEASE</version
>
</dependency> </dependencies>

3. 程式碼

3.1 DBProperties
    @Data
    public class DBProperties {
    
        private String driverName = "com.mysql.jdbc.Driver";
    
        private String url = "jdbc:mysql://localhost:3306/mp?useSSL=false";
    
        private String userName =
"root"; private String password = "root"; /** * 連線池名字 */ private String poolName = "database-pool-"; /** * 空閒池,最小連線數 */ private int minConnections = 5; /** * 空閒池,最大連線數 */ private int maxConnections = 10; /** * 初始化連線數 */ private int initConnections = 2; /** * 重複獲得連線的頻率 */ private long connTimeOut = 1000; /** * 連線超時時間,預設20分鐘 */ private long connectionTimeOut = 1000 * 60 * 20; }
3.2 ConnectionPoolManager
    public class ConnectionPoolManager {
    
        /**
         * 空閒連線集合
         */
        private List<Connection> freeConnection = new Vector<>();
    
        /**
         * 活動連線集合
         */
        private List<Connection> activeConnection = new Vector<>();
    
        /**
         * 連線數
         */
        private AtomicInteger connectCount = new AtomicInteger(0);
    
        /**
         * 資料庫配置資訊
         */
        private DBProperties dbProperties = new DBProperties();
    
        public ConnectionPoolManager() {
            this.init();
        }
    
        /**
         * 獲取連線
         *
         * @return
         * @throws Exception
         */
        public synchronized Connection getConnection() throws Exception {
            Connection connection = null;
            if (activeConnection.size() >= dbProperties.getMaxConnections()) {
                //如果達到了最大連線,則等待 connTimeOut
                System.out.println("當前活動連線已經達到了最大連線:" + activeConnection.size() + ",等待ing");
                wait(dbProperties.getConnectionTimeOut());
                connection = this.getConnection();
            } else {
                //如果未達到最大連線數,則返回 connection
                if (freeConnection.size() > 0) {
                    //如果有空閒連線
                    System.out.println("當前空閒連線還有:" + freeConnection.size() + ",從空餘連線池中獲取連線");
                    connection = freeConnection.remove(0);
                } else {
                    //沒有空閒連線
                    connection = this.newConnection();
                    System.out.println("當前沒有空閒連線,利用 JDBC 技術獲取連線");
                }
                activeConnection.add(connection);
            }
            return connection;
        }
    
        /**
         * 釋放連線
         *
         * @param connection
         * @throws Exception
         */
        public synchronized void releaseConnection(Connection connection) throws Exception {
            if (!isAvailable(connection)) {
                return;
            }
            if (freeConnection.size() <= dbProperties.getMinConnections()) {
                //如果當前連線小於最小連線數
                System.out.println("當前空閒連線還有:" + freeConnection.size() + ",不大於 minConnections 配置:" + dbProperties.getMinConnections() + ",重新放入到空閒連線池中");
                freeConnection.add(connection);
            } else {
                //如果大於最小連線數,則關閉
                connection.close();
                this.connectCount.decrementAndGet();
                System.out.println("當前空閒連線還有:" + freeConnection.size() + ",大於 minConnections 配置:" + dbProperties.getMinConnections() + ",銷燬連線");
            }
            activeConnection.remove(connection);
            notifyAll();
        }
    
        /**
         * 初始化連線
         */
        private void init() {
            Connection connection = null;
            for (int i = 0; i < dbProperties.getInitConnections(); i++) {
                connection = this.newConnection();
                freeConnection.add(connection);
            }
            System.out.println("資料庫連線池初始化完成:共有 " + freeConnection.size() + " 個連線");
        }
    
        /**
         * jdbc 獲取連線
         *
         * @return
         */
        private Connection newConnection() {
            try {
                if (dbProperties == null) {
                    return null;
                }
                Class.forName(dbProperties.getDriverName());
                Connection connection = DriverManager.getConnection(dbProperties.getUrl(), dbProperties.getUserName(), dbProperties.getPassword());
                connectCount.incrementAndGet();
                System.out.println("從資料庫中獲取連線:" + connection + ",當前連線數:" + connectCount);
                return connection;
            } catch (Exception e) {
                return null;
            }
        }
    
        /**
         * 判斷連線是否可用
         *
         * @param connection
         * @return
         */
        private boolean isAvailable(Connection connection) {
            try {
                if (connection == null || connection.isClosed()) {
                    return false;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return true;
        }
    
    }

4. 測試

    public class TestThreadPool {
    
        public static void main(String[] args) {
            final ConnectionPoolManager connectionPoolManager = new ConnectionPoolManager();
            for (int i = 0; i < 15; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            for (int y = 0; y < 10; y++) {
                                Connection connection = connectionPoolManager.getConnection();
                                Thread.sleep(1);
                                connectionPoolManager.releaseConnection(connection);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }, "使用者執行緒:" + i).start();
            }
        }
    
    }

5. 專案結構圖

專案結構