common-pool:
對於一些對象的頻繁創建會帶來很大的系統開銷,並且需要對對象數量進行控制來降低資源消耗,比如數據庫連接,線程等
common-pool采用了緩存思想來解決這個問題,預先把一些對象資源創建好並統一保存起來,也就是保存到邏輯上的對象池中
等到需要對象時從池中直接獲取,不需要時歸還到池中
目前對象池技術已經有很多開源優秀的庫了,比如:Java實現的Apache Commons Pool、Go Commons Pool
go-common-pool就是參照Apache Commons Pool的思想,用go語言實現的一個通用對象池
原理分析
幾個重要的數據結構介紹
factory:
包含了一個通用對象常用的方法集合,可以理解為一個接口,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. 獲取對象時支持阻塞非阻塞兩種方式