【進階之路】和南橘一塊兒探索鏈接池(一)

1、鏈接池的定義

什麼叫鏈接池?顧名思義,鏈接池就是將應用所需的鏈接對象放在池中,每次訪問時從池中獲取,使用完畢再放回池中,以達到鏈接複用的目的。鏈接池和線程池很像,都是爲了減小鏈接對象在建立、銷燬鏈接過程當中沒必要要消耗的資源java

你們接觸最多的鏈接池、大概是數據庫鏈接或者tomcat鏈接池,C3P0、DBCP、Tomcat Jdbc Pool、BoneCP、Druid等。這些鏈接池的目的都很是的純粹,即在服務啓動的時候,預先生成若干條鏈接,每當有請求過來,就從中取出一個,執行操做,執行完成後再放回,從而避免反覆的創建和銷燬鏈接,以提高性能mysql

鏈接池性能對比sql

實際在微服務中,鏈接池是很是重要的組件,由於服務間須要創建鏈接通訊,經過鏈接池能夠極大地提升服務間的通訊性能。數據庫

通常鏈接池的實現,圖片來自拉鉤教育

所以,咱們只要創建多條鏈接,用一個數組維護多條鏈接就好了;若是使用一條鏈接,那麼從數組裏拿出這條鏈接,使用完再放入數組便可。當數組爲空時,只要創建新的鏈接就能夠了。後端

2、自定義鏈接池

具體的實現能夠參考我下文實現的代碼。這個鏈接池擁有了幾個基本數據。數組

  • maxIdleConns 最大空閒鏈接數,這個值至關於線程池裏的核心線程數、與線程池不同的是,配置了最大空閒數後,鏈接池的鏈接將會長期保持。這個值的設置頗有講究,須要結合後端服務的鏈接承載能力設置tomcat

  • currentCount 當前使用數目,相似於核心線程數到最短線程數以前的這個值。微信

  • maxIdCount 最大鏈接數,表明着鏈接池的上限。多線程

  • ttls 鏈接的過時時間,這個時間線程池中也有,若是鏈接都爲空閒鏈接,則不會進行過時處理。併發

public class MyPool {

    // 最大鏈接數 
    private int maxIdCount;
    //記錄當前使用 的鏈接數目
    private int currentCount ;
    //初始化線程數、最大空閒鏈接數
    private int maxIdleConns;
	// 多久後鏈接會斷開,不少鏈接池會在必定時間後斷開鏈接,而後將新鮮的鏈接放入鏈接池
    private int ttls;

    //咱們用LinkedList來定義一個鏈接池
    private LinkedList<Connections> connectionPool;

    public MyPool(int maxIdleConns, int currentCount,int maxIdCount,int ttls) {
        this.maxIdleConns = maxIdleConns;
        this.currentCount = currentCount;
        this.maxIdCount =maxIdCount;
        this.ttls=ttls;
        this.connectionPool = new LinkedList<Connections>();

        for(int i=0;i<maxIdleConns;i++){
            connectionPool.add(createConnection());
        }
    }
    /**
     * 創建一個連接
     * @return  Connection
     */
    public    Connections createConnection(){
        try {

            Class.forName("com.mysql.jdbc.Driver");
            return new Connections(DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root"), DateUtil.getCurrTime());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 先判斷鏈接池中是否有鏈接,若是有直接使用,若是沒有鏈接,
     * 則先判斷當前的鏈接是否達到最大鏈接數,達到的話 拋出異常,沒有達到最大鏈接 則建立鏈接
     * @return
     */
    public Connections getConnection(){
        //1.判斷,池中如有鏈接直接使用
        if(connectionPool.size()>0){
            //把這個連接移出集合並返回當前鏈接對象。
            currentCount++;
            return connectionPool.removeFirst();

        }
        //若是池中沒有鏈接並且沒有達到最大鏈接數目;則建立鏈接
        if(currentCount>=maxIdleConns && currentCount<maxIdCount){
            currentCount++;
            //建立一個新的鏈接
            return createConnection();
        }
        //判斷是否達到最大鏈接數,達到則拋出異常
        System.out.println();
        throw new RuntimeException("當前鏈接已經達到最大鏈接數!");
    }


    /**
     * 把鏈接放回鏈接池(集合)中。若是池中鏈接數小初始化鏈接數目就放回池中,其餘則關閉鏈接。
     * @param conn
     */
    public void releaseConnection(Connections conn){
        //判斷池中的數目若是小於初始化鏈接就放回鏈接池中
        //判斷鏈接池中的剩餘數目是否<鏈接池初始化數目   若是爲真 則放回鏈接池
        if(currentCount<=maxIdCount){
            //放回鏈接池
            connectionPool.addLast(conn);
            //當前鏈接-1
            currentCount--;

        }else{
            //關閉鏈接
            try {
                conn.connection.close();
                currentCount--;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 清除所有超時鏈接
     * 初始化線程數無需釋放
     * @param connectionPool
     */
    public void release(LinkedList<Connections> connectionPool){
        if (connectionPool.size()<1||currentCount==0){
            return;
        }
        if ( ttls<(DateUtil.getCurrTime()-connectionPool.getFirst().getTtl())){
            Connections conn = connectionPool.getFirst();
            //當前鏈接-1
            currentCount--;
            connectionPool.removeFirst();
            //關閉鏈接
            try {
                conn.connection.close();
                currentCount--;
            } catch (SQLException e) {
                e.printStackTrace();
            }
            release(connectionPool);
        }
    }
}

3、鏈接池須要的問題

一、併發問題

上面的代碼只是我簡單實現了一下鏈接池,可是爲了使鏈接管理服務具備最大的通用性,必須考慮多線程環境,即併發問題。在java中,有各類鎖的機制可以解決鏈接池的併發問題。

二、鏈接池大小的設置

若是設置得太大,假如設置 1000,始發集羣有 100 臺機器,那麼就會創建 10w 的持久鏈接,這對後端服務的壓力可想而知。但也不能設置得過小。

若是鏈接池滿了,就會創建新的鏈接,不斷創建的新鏈接會耗光後端服務的資源。

新創建的鏈接在用完以後,有兩種選擇——鏈接池有餘量的狀況會放入鏈接池,反之會直接丟棄,這種狀況在瞬間很容易出現,鏈接池持續瞬間被空閒鏈接佔滿(最大空閒鏈接數的叫法也由此得來),致使新鏈接沒法放回鏈接池,進而丟棄,這樣就會造成創建鏈接—用完丟棄的惡性循環,鏈接池的做用也就消失了。對於HTTP請求的鏈接池來講,全部的鏈接都退化成了短鏈接。

實際上鍊接並無長短之分,只是取決於傳輸完數據後是否斷開。那麼爲何會有長短鏈接的叫法呢?這是由於 HTTP 協議多用於 Web 中,Web 的交互方式可能是一來一回的模式,這樣的應用場景下,不須要服務端推數據,因此創建鏈接後當即釋放也是徹底能夠的。

三、鏈接池的分配與釋放

鏈接池的分配與釋放,對系統的性能有很大的影響。合理的分配與釋放,能夠提升鏈接的複用度,從而下降創建新鏈接的開銷,同時還能夠加快用戶的訪問速度。
咱們這個鏈接池是使用LinkedList來實現的,主要的目的是考慮到過時時間。在鏈表中,前面鏈接的持續時間必定高於後面的鏈接,也能夠減小鏈接的輪循時間。

你們好,我是練習java兩年半時間的南橘,下面是個人微信,須要以前的導圖或者想互相交流經驗的小夥伴能夠一塊兒互相交流哦。

相關文章
相關標籤/搜索