連接池的實現要點總結

序言: 因爲ssdb(一種nosql數據庫) java客戶端沒有實現連接池,需要模仿jdbc裏面的連接池的思想,實現一個簡單的連接池,以下是自己的總結:

思想: 每new 一個SSDB實例 ,用完之後close,其實底層就是開了一個socket連接,又關閉了,這其實是相當的消耗資源的,所以連接池的作用就是重用。不是直接關閉而是放到一個容器中去,下次再用的時候,直接從這個容器中拿。那麼要點以下幾點:

  1. 容器的選擇:是LinkList,,它不同於ArrayList,LinkList是一種線性的結構保持FIFO,相當於一個隊列,優點是可以實現快速的插入和移除。
  2. 初始化連接數:可以在第一次初始化的時候就先創建了一定數量的連接,這其實是比較大的開銷,但是爲後面的快速調用是非常值得的。
  3. 最大連接數:當併發很大的時候,就需要大量的鏈接,當從池子裏面取不到連接時,就會創建新的鏈接,任何機器都是有它的極限值,爲了不至於把資源耗完,我們就只能限制這個極限值(最大連接數),一旦大於這個極限值的時候,我就拒絕提供鏈接,直接拋出異常(當然這裏可以有很多種處理方式,比如阻塞進程,讓它等待,直到超時)。
  4. 超時,最小空閒數:當一個鏈接在隊列中空閒着,超過一定的時間,就會把它銷燬掉,但是還是會保留一個最小的空閒數量(我這裏沒有實現)。
    package ssdb.datasource;
    
    import com.udpwork.ssdb.SSDB;
    
    import java.util.LinkedList;
    
    /**
     * Desc:
     * User: weiguili([email protected])
     * Date: 14-1-15
     * Time: 上午9:18
     */
    public class DataSource {
        private String host = "192.168.98.250";
        private int port = 9966;
        private int timeoutMs = 5000;
        private int minIdle = 10;//最小空閒數
        private int iniCount = 5;//初始化連接
        private int maxCount = 10;//最大連接數
    
        int currentCount = 0;//當前連接數
        LinkedList<SSDB> ssdbConnectionsPool = new LinkedList<SSDB>();
    
        public DataSource() {
            try {
                for(int i=0;i<iniCount;i++){
                    currentCount++;
                    ssdbConnectionsPool.addLast(createSSDB(null));
                }
            } catch (Exception e) {
                throw new ExceptionInInitializerError(e);
            }
        }
    
        public SSDB createSSDB(ThreadLocal<SSDB> cacheSSDB) throws Exception {
            return new MySSDB(host,port,timeoutMs,this,cacheSSDB);
        }
    
        public SSDB getSSDB(ThreadLocal<SSDB> cacheSSDB) throws Exception{
            synchronized (ssdbConnectionsPool){
                if(ssdbConnectionsPool.size()>0){
                    return ssdbConnectionsPool.removeFirst();
                }
                if(currentCount<maxCount){
                    currentCount++;
                    return createSSDB(cacheSSDB);
                }
                throw new Exception("沒有連接了");
            }
        }
    }
    


  5. 覆蓋close方法:這裏爲了不改變用戶的使用習慣,我需要覆蓋原來的SSDB中的close方法,我這裏使用的是繼承(在這種情況下,我的靈活性要求不大,JDBC裏面用的是代理,最好是動態代理,實際上體現的是組合優於繼承思想)。
    package ssdb.datasource;
    
    import com.udpwork.ssdb.SSDB;
    
    /**
     * Desc:
     * User: weiguili([email protected])
     * Date: 14-1-15
     * Time: 上午10:02
     */
    public class MySSDB extends SSDB {
        private DataSource dataSource;
        private int reusedCount = 20;//可重用次數
        private int currentReusedCount = 0;//當前重用次數
        private ThreadLocal<SSDB> cacheSSDB = null;
    
        MySSDB(String host,int port,int timeoutMs, DataSource dataSource,ThreadLocal<SSDB> cacheSSDB) throws Exception{
            super(host,port,timeoutMs);
            this.dataSource = dataSource;
            this.cacheSSDB = cacheSSDB;
        }
    
        @Override
        public void close() {
            currentReusedCount++;
            if(currentReusedCount<reusedCount){
                this.dataSource.ssdbConnectionsPool.addLast(this);
            }else {
                this.dataSource.currentCount--;
                if(cacheSSDB !=null && cacheSSDB.get() !=null)
                    cacheSSDB.remove();//當連接關閉時,把緩存也幹掉
                super.close();
            }
        }
    }
    


  6. SSDBUtil,ThreadLocal:最後是需要提供一個工具類SSDBUtil,目的是把對連接池的使用,都放到一個靜態域裏面去,實際上就是讓它實現單例。在這裏有一點非常關鍵,ThreadLocal是個線程的本地變量,在這裏把每個SSDB包一下放進去可以起到線程之間隔離開來,各自處理自己的連接,避免了資源的搶佔,是解決併發的一種解決方案。
    package ssdb;
    
    import com.udpwork.ssdb.SSDB;
    import ssdb.datasource.DataSource;
    
    /**
     * Desc:
     * User: weiguili([email protected])
     * Date: 14-1-15
     * Time: 上午10:42
     */
    public class SSDBUtil {
        private static DataSource dataSource = new DataSource();
        static ThreadLocal<SSDB> cacheSSDB = new ThreadLocal<SSDB>();//包一下,線程之間隔離
        public static SSDB getSSDB() throws Exception{
            if(cacheSSDB.get() != null){
                return cacheSSDB.get();
            }
            SSDB ssdb = dataSource.getSSDB(cacheSSDB);
            cacheSSDB.set(ssdb);
            return ssdb;
        }
        /*public static SSDB getSSDB() throws Exception{
            return dataSource.getSSDB(cacheSSDB);
        }*/
    }
    
  7. 最後附上一張連接池的原理圖:

  8. 示例代碼:
    package ssdb;
    
    import com.udpwork.ssdb.*;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.Executors;
    
    /**
     * SSDB Java client SDK demo.
     */
    public class Demo {
    	public static void main(String[] args) throws Exception {
    		SSDB ssdb = null;
    		Response resp;
    		byte[] b;
    		ssdb = SSDBUtil.getSSDB();
    		
    		/* kv */
    		System.out.println("---- kv -----");
    
            //ssdb.set("a", "123");
            ssdb.set("a", "122");
            b = ssdb.get("a");
            System.out.println(new String(b));
    		ssdb.del("a");
    		b = ssdb.get("a");
            ssdb.set("a","90");
    		System.out.println(b);
    	    long incr = ssdb.incr("a", 10);
            System.out.println("-------increment the number by--------");
            System.out.println(incr);
    		
    		resp = ssdb.scan("", "", 10);
    		resp.print();
    		resp = ssdb.rscan("", "10000", 10);
    		resp.print();
    		System.out.println("");
    
    		/* hashmap */
    		System.out.println("---- hashmap -----");
    
    		ssdb.hset("n", "a", "123");
    		b = ssdb.hget("n", "a");
            System.out.println(new String(b));
            ssdb.hdel("n", "a");
            b = ssdb.hget("n", "a");
            System.out.println(b);
            ssdb.hincr("n", "a", 10);
            ssdb.hset("n", "d", "124");
            ssdb.hset("n", "c", "124");
            ssdb.hset("n", "b", "124");
    
    
    		resp = ssdb.hscan("n", "a", "z", 10);
    		//resp = ssdb.hrscan("n", "", "", 10);
    		resp.print();
    		System.out.println("");
    
    		/* zset */
    		System.out.println("---- zset -----");
    
    		double d;
            ssdb.zset("hackers", "Alan Kay", 1940);
            ssdb.zset("hackers", "Richard Stallman", 1953);
            ssdb.zset("hackers", "Yukihiro Matsumoto", 1965);
            ssdb.zset("hackers", "Claude Shannon", 1916);
            ssdb.zset("hackers", "Linus Torvalds", 1999);
            ssdb.zset("hackers", "Alan Turing", 1912);
    
    		ssdb.zset("n", "a", 1);
    		d = ssdb.zget("n", "a");
    		System.out.println(d);
    		ssdb.zdel("n", "a");
    		d = ssdb.zget("n", "a");
    		System.out.println(d);
            ssdb.zincr("n", "b", 10);
    
            //resp = ssdb.zscan("hackers", "", Double.valueOf(1912), Double.valueOf(1999), 10);
            resp = ssdb.zscan("test", "", null, Double.MAX_VALUE, 10);
    		resp.print();
            System.out.println(resp.items);
            System.out.println(resp.keys);
            System.out.println(resp.raw);
    		System.out.println("");
    		
    		/* multi */
    		ssdb.multi_set("a", "1b", "b", "2b");
    		resp = ssdb.multi_get("a", "b");
    		resp.print();
    		System.out.println("");
    	
    		//
    		ssdb.close();
    
            concurrentTest();
    	}
    
        public static void concurrentTest(){
            Executor pool = Executors.newFixedThreadPool(50);
            for(int i=0;i<1000;i++){
                Runnable task = new Runnable() {
                    @Override
                    public void run() {
                        synchronized (this){
                            System.out.println(Thread.currentThread().getName());
                            SSDB ssdb = null;
                            try {
                                ssdb = SSDBUtil.getSSDB();
                                System.out.println(ssdb);
                            } catch (Exception e) {
                                e.printStackTrace();
                            } finally {
                                ssdb.close();
                                //ssdb.close();
                            }
                        }
                    }
                };
                pool.execute(task);
            }
        }
    }
    








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章