使用Go語言實現(xiàn)一個簡單的無界資源池
寫在文章開頭
我們希望通過go語言實現(xiàn)一個簡單的資源池,而這個資源池的資源包括但不限于:
- 數(shù)據(jù)庫連接池
- 線程池
- 協(xié)程池
- 網(wǎng)絡連接池
只要這些資源實現(xiàn)我們指定的關閉方法,則都可以通過我們封裝的資源池進行統(tǒng)一管理,需要簡單說明一下這個資源池的要求:
- 需要用戶指定資源以及資源的創(chuàng)建方法。
- 當協(xié)程通過
Acquire
方法獲取資源時,若發(fā)現(xiàn)當前池中有資源可以分配則直接返回,若沒有足夠的資源則基于傳入的創(chuàng)建方法創(chuàng)建一個全新的資源分配。 - 支持資源釋放和資源池關閉。
聽起來很像是Java的無界線程池,接下來我們就基于這個需求實現(xiàn)一個版本。
需求落地
給出資源池結構
我們首先需要給出資源池的結構,很明顯作為一個資源池它需要有一個管理資源池的channel,為了保證多協(xié)程競爭資源的協(xié)程安全,我們還需要通過一把Mutex完成操作互斥,同時給出創(chuàng)建資源的工廠方法要求這個工廠方法創(chuàng)建的資源具備資源關閉能力:
// Pool 定義一個結構體 包含重量級鎖 有緩沖區(qū)Chanel 工廠方法 連接池關閉狀態(tài) type Pool struct { m sync.Mutex resource chan io.Closer factory func() (io.Closer, error) closed bool }
創(chuàng)建資源池
有個上述的定義之后,我們的創(chuàng)建方法就很容易實現(xiàn)了,只需基于外部的size和工廠方法完成Pool成員變量初始化即可:
var ErrPoolClosed = errors.New("連接池已關閉") func New(fn func() (io.Closer, error), size uint) (*Pool, error) { //判斷size大小是否合法 if size <= 0 { return nil, errors.New("size不合法") } //基于工廠方法和size創(chuàng)建資源池 return &Pool{ resource: make(chan io.Closer, size), factory: fn, }, nil }
獲取資源
當協(xié)程需要獲取資源時,會查看當前緩沖通道是否有足夠的資源,如果有則在正確運行的情況下返回出去,反之基于我們上文傳入的工廠方法完成資源創(chuàng)建并返回:
func (p *Pool) Acquire() (io.Closer, error) { select { //如果channel有足夠的資源分配則直接返回 case r, ok := <-p.resource: if !ok { log.Println("連接池已關閉") return nil, ErrPoolClosed } log.Println("拿到連接池共享資源") return r, nil //基于工廠方法創(chuàng)建全新的資源返回出去 default: log.Println("資源不足,創(chuàng)建新的連接資源") return p.factory() } }
釋放與關閉
這里我們將資源的釋放和關閉放在一起說明,在進行資源釋放和關閉時我們需要考慮3個問題即:
- 已關閉的資源池無需歸還資源。
- 正在關閉資源池時不可歸還資源。
- 正在歸還資源時不可關閉資源池。
所以進行這兩個操作時,我們需要通過互斥鎖確保兩個操作互斥:
// Release 上鎖 設置方法退出后解鎖 查看當前連接池是否已關閉,若關閉則直接將資源關閉 ,反之select查看能否將其存入緩沖區(qū),若可以輸出入隊成功,反之輸出隊列已滿 func (p *Pool) Release(r io.Closer) { //上鎖確保關閉和歸還資源操作互斥 p.m.Lock() //函數(shù)退出時解鎖 defer p.m.Unlock() //如果資源池關閉則直接將當前資源關閉銷毀 if p.closed { log.Println("連接池已關閉,直接銷毀當前資源") r.Close() } //將連接歸還,如果滿了則直接關閉銷毀 select { case p.resource <- r: log.Println("連接歸還成功") default: log.Println("連接池已滿,資源直接銷毀") r.Close() } } // Close 方法 上鎖 設置方法退出后解鎖 遍歷所有資源將其關閉 然后再關閉連接池 func (p *Pool) Close() { p.m.Lock() defer p.m.Unlock() if p.closed { log.Println("連接池已關閉,直接銷毀當前資源") return } //設置為關閉 p.closed = true //關閉資源 close(p.resource) //遍歷資源池資源 for r := range p.resource { r.Close() } }
測試代碼與輸出
最后我們給出測試代碼,可以看到我們基于資源池工具類模擬數(shù)據(jù)庫連接池的管理:
//設置最大協(xié)程數(shù)與資源池數(shù)為24 const maxGoroutines = 24 const poolResources = 24 //創(chuàng)建可關閉的數(shù)據(jù)庫連接 type dbConnection struct { ID int32 } //對應的關閉方法 func (d *dbConnection) Close() error { log.Println("當前數(shù)據(jù)庫連接", d.ID, "已關閉") return nil } var idCounter int32 func createConnection() (io.Closer, error) { id := atomic.AddInt32(&idCounter, 1) return &dbConnection{ID: id}, nil } func main() { //創(chuàng)建maxGoroutines個WaitGroup var wg sync.WaitGroup wg.Add(maxGoroutines) //傳入createConnection方法和連接池大小poolResources創(chuàng)建數(shù)據(jù)庫連接池 p, err := pool.New(createConnection, poolResources) if err != nil { log.Println(err) } //創(chuàng)建24個協(xié)程獲取資源 for i := 0; i < maxGoroutines; i++ { go func(queryParam int) { queryData(queryParam, p) defer wg.Done() }(i) } //等待操作完成關閉連接池 wg.Wait() log.Println("查詢完成") p.Close() } //queryData 基于連接池Acquire獲取資源,完成后通過Release歸還資源 func queryData(queryParam int, p *pool.Pool) { r, e := p.Acquire() if e != nil { log.Println(e) return } defer p.Release(r) time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) log.Println("查詢", queryParam, "使用連接", r.(*dbConnection).ID) }
同時我們給出輸出結果:
2024/05/05 23:36:10 資源不足,創(chuàng)建新的連接資源
2024/05/05 23:36:10 資源不足,創(chuàng)建新的連接資源
2024/05/05 23:36:10 資源不足,創(chuàng)建新的連接資源
2024/05/05 23:36:10 資源不足,創(chuàng)建新的連接資源
2024/05/05 23:36:10 資源不足,創(chuàng)建新的連接資源
2024/05/05 23:36:10 資源不足,創(chuàng)建新的連接資源
2024/05/05 23:36:10 資源不足,創(chuàng)建新的連接資源
2024/05/05 23:36:10 資源不足,創(chuàng)建新的連接資源
2024/05/05 23:36:10 資源不足,創(chuàng)建新的連接資源
2024/05/05 23:36:10 資源不足,創(chuàng)建新的連接資源
2024/05/05 23:36:10 資源不足,創(chuàng)建新的連接資源
2024/05/05 23:36:10 查詢 17 使用連接 14
2024/05/05 23:36:10 連接歸還成功
2024/05/05 23:36:10 查詢 5 使用連接 5
2024/05/05 23:36:10 連接歸還成功
2024/05/05 23:36:10 查詢 3 使用連接 2
2024/05/05 23:36:10 連接歸還成功
2024/05/05 23:36:10 查詢 19 使用連接 19
2024/05/05 23:36:10 連接歸還成功
.......
小結
到此這篇關于使用Go實現(xiàn)一個簡單的無界資源池的文章就介紹到這了,更多相關Go無界資源池內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
golang執(zhí)行命令獲取執(zhí)行結果狀態(tài)(推薦)
這篇文章主要介紹了golang執(zhí)行命令獲取執(zhí)行結果狀態(tài)的相關知識,非常不錯,具有一定的參考借鑒價值,需要的朋友參考下吧2019-11-11Golang標準庫os/exec執(zhí)行外部命令并獲取其輸出包代碼示例
這篇文章主要為大家介紹了Golang標準庫os/exec執(zhí)行外部命令并獲取其輸出包代碼示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12