鏈接池原來這麼簡單(一分鐘系列)

應網友要求,寫一寫鏈接池實現細節。數據庫

1、如何經過鏈接訪問下游

工程架構中有不少訪問下游的需求,下游包括但不限於服務/數據庫/緩存,其通信步驟是爲:
(1)與下游創建一個鏈接
(2)經過這個鏈接,收發請求
(3)交互結束,關閉鏈接,釋放資源數組

這個鏈接是什麼呢,經過鏈接怎麼調用下游接口?服務/數據庫/緩存,官方會提供不一樣語言的Driver、Document、DemoCode來教使用方創建鏈接與調用接口,以MongoDB的C++官方Driver API爲例(僞代碼):緩存

DBClientConnection* c = new DBClientConnection();
c->connect(「127.0.0.1:8888」);
c->insert(「db.s」, BSON(」shenjian」));
c->close();

鏈接池原來這麼簡單(一分鐘系列)
這個DBClientConnection就是一個與MongoDB的鏈接,官方Driver經過它提供了若干API,讓用戶能夠對MongoDB進行鏈接,增刪查改,關閉的操做,從而實現不一樣的業務邏輯。數據結構

2、爲何須要鏈接池

當併發量很低的時候,上述僞代碼沒有任何問題,但當服務單機QPS達到幾百、幾千的時候,創建鏈接connect和銷燬鏈接close就會成爲瓶頸,此時該如何優化?架構

結論也很簡單,服務啓動的時候,先創建好若干鏈接Array[DBClientConnection],當有請求過來的時候,從Array中取出一個,執行下游操做,執行完再放回,從而避免反覆的創建和銷燬鏈接,以提高性能。併發

而這個對Array[DBClientConnection]進行維護的數據結構,就是鏈接池。有了鏈接池以後,數據庫操做的僞代碼變爲:
DBClientConnection* c = ConnectionPool::GetConnection();
c->insert(「db.s」, BSON(」shenjian」));
ConnectionPool::FreeConnection(c);負載均衡

3、鏈接池核心接口與實現

經過上面的討論,能夠看到鏈接池ConnectionPool主要有三個核心接口:
(1)Init:初始化好Array[DBClientConnection],這個接口只在服務啓動時調用一次
(2)GetConnection:請求每次須要訪問數據庫時,不是connect一個鏈接,而是經過鏈接池的這個接口來拿
(3)FreeConnection:請求每次訪問完數據庫時,不是close一個鏈接,而是把這個鏈接放回鏈接池ide

鏈接池核心數據結構:
(1)鏈接數組Array DBClientConnection [N]
(2)互斥鎖數組Array lock[N]性能

鏈接池核心接口實現:優化

Init(){
 for i = 1 to N {
  Array DBClientConnection [i] = new();
  Array DBClientConnection [i]->connect();
  Array lock[i] = 0;
 }
}

說明:把全部鏈接和互斥鎖初始化

GetConnection()
 for i = 1 to N {
  if(Array lock[i] == 0){
   Array lock[i] = 1;
   return Array DBClientConnection[i];
   }
 }
}

說明:找一個可用的鏈接,鎖住,並返回鏈接

FreeConnection(c)
 for i = 1 to N {
 if(Array DBClientConnection [i] == c){
   Array lock[i] = 0;
   }
  }
}

說明:找到鏈接,把鎖釋放

鏈接池原來這麼簡單(一分鐘系列)
能夠發現,簡單的鏈接池管理並非很複雜,基本原理即如上所述。

4、未盡事宜

上述僞代碼忽略了一些細節,在實現鏈接池中是須要考慮的:
(1)若是鏈接所有被佔用,是返回失敗,仍是讓上游等待
(2)須要實施鏈接可用性檢測
(3)爲了讓調用方更友好,可能還須要包裝一層DAO層,讓「鏈接」這個東西對調用方都是黑盒的
(4)經過freeArray,connectionMap可讓取鏈接和放回鏈接都達到O(1)時間複雜度
(5)能夠經過hash實現id串行化
(6)負載均衡、故障轉移、服務自動擴容均可以在這一層實現

但願這一分鐘你們有收穫。==【完】==相關閱讀:「id串行化」究竟是怎麼實現的?消息「時序」與「一致性」爲什麼這麼難?

相關文章
相關標籤/搜索