go-common-pool設計原理分析


common-pool:

對於一些對象的頻繁創建會帶來很大的系統開銷,並且需要對對象數量進行控制來降低資源消耗,比如數據庫連接,線程等

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

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

目前對象池技術已經有很多開源優秀的庫了,比如:Java實現的Apache Commons PoolGo 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. 獲取對象時支持阻塞非阻塞兩種方式 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM