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