go-common-pool設計原理分析

common-pool:git

對於一些對象的頻繁建立會帶來很大的系統開銷,而且須要對對象數量進行控制來下降資源消耗,好比數據庫鏈接,線程等github

common-pool採用了緩存思想來解決這個問題,預先把一些對象資源建立好並統一保存起來,也就是保存到邏輯上的對象池中數據庫

等到須要對象時從池中直接獲取,不須要時歸還到池中apache

目前對象池技術已經有不少開源優秀的庫了,好比:Java實現的Apache Commons PoolGo Commons Pool緩存

go-common-pool就是參照Apache Commons Pool的思想,用go語言實現的一個通用對象池安全

 

原理分析數據結構

幾個重要的數據結構介紹socket

factory:spa

包含了一個通用對象經常使用的方法集合,能夠理解爲一個接口,client能夠根據不一樣的對象特性自定義操做線程

 

ObjectPool:

主要包含了對象池的一些屬性方法 

idleObjects是一個雙向隊列結構,保存一些可用對象,能夠用FIFO、LIFO方式訪問對象

allObjectsy是一個map結構,根據key-value形式保存對象,主要用來校驗對象是否存在合法

也就是說一個對象在對象池中保存兩份數據,對象池會啓動一個協程定時維護對象

 

config:

包含對象池的一些配置

 

初始化過程

啓動一個go run去定時維護對象池中的對象集合,主要分如下幾步

第一步:檢查空閒對象集合剔除即將失效或者已經失效對象(經過factory.Validate()判斷),不會全量檢查只會檢查必定數量或者比例的空閒對象(數量可配置)

第二步:遍歷allObjects集合中的全部對象,剔除失效對象

第三步:判斷idleObjectsCount是否大於MinIdleCount,若是小於則建立對象,並往空閒對象隊列裏添加節點,直到兩者相等

疑問:兩者是同一個分支執行過程,既然有了第一部分的檢查,不知道第二步存在的意義是什麼。

 

獲取對象:BorrowObject

首先會去idleObjects隊列裏面獲取一個對象,若是爲空說明隊列裏面沒有現成的對象,則去建立一個新的對象

在建立對象的過程當中會先更新createCount,而後判斷createCount是否大於MaxTotal

若是小於則繼續建立對象,調用factory.Make()建立真正的對象,更新allObjects集合,若是建立成功返回新對象,若是失敗返回nil

若是大於則說明對象池容量已經達到最大值,

這時候有兩種選擇,一種是返回錯誤,一種是先阻塞當前協程,等到其餘協程歸還對象後發送一個信號,喚醒當前協程最終獲取到對象

第二種方式中支持兩個設置:

一種是無限等待時間直到有錯誤產生,監聽一種channel便可

一種是指定超時時間,超時後返回空對象和錯誤,須要用select監聽兩個channel,除了與其餘協程通訊channel以外,還須要一個Timer.C channel時間超時channel

僞代碼能夠簡化爲:

func takeWhithTimeout(){
  for a:=next();a==nil;a=next() { if 沒有剩餘時間了 return nil if 能夠終止了 return nil a.f()   }
  return a
} func (a Node) f() {   select {     case <-a.ch: //通知channel,獲取到通知說明其隊列裏已經有了可用對象了,同時計算一下執行當前case後還有多少時間超時       return 剩餘時間     case <-Time.After(timeout): //時間超時channel       return 超時了能夠終止了   } }

  

歸還對象:ReturnObject

1. 去allObjects去檢查歸還對象是否合法存在

2. 校驗歸還對象是否有效,調用factory.Validate()

3. 判斷idleObjectsCount是否大於MaxIdleCount,若是小於則添加對象到idleObjects隊列,不然銷燬對象

 

總結:

go-common-pool對象池中還有不少細節沒有體現出來,上面只有對整個對象池幾個重要的操做進行了一個簡單版的描述,後續會更新一些細節實現上的技巧

在這裏我想總結下我的認爲比較重要的幾點

1. 一個對象的操做make(建立)-->active(激活)-->validate(校驗)-->destory(銷燬)-->passivate(鈍化)

   建立、校驗操做是很容易理解也是很經常使用的兩個操做,通常對象定義好這兩個操做就能夠了。

   激活、鈍化不容易理解,這兩個操做是相對的,一個是初始化對象,一個是反初始化對象,消除以前對象佔用的一些資源,

   具體須要結合應用場景,不一樣的對象類型好比數據庫鏈接,socket鏈接,線程對應不一樣的操做,在翻閱了一些資料後還沒找到一個很好的解釋。

2. poolObject是整個對象池很核心的一個對象,對外鏈接factory的對象操做,對內鏈接allObjects、idleObjects集合的操做

    idleObjects/allObjects都是經過鎖機制來保證線程安全操做的

    幾個重要事件:

    一個對象加入idleObjects時須要進行(鈍化)操做

    一個對象在獲取時須要進行(激活-->校驗)操做

    一個對象在歸還時須要進行(校驗-->鈍化)操做

    其中任何一步失敗都須要銷燬對象

3. 獲取對象時支持阻塞非阻塞兩種方式 

相關文章
相關標籤/搜索