iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 25
0
自我挑戰組

Go劍復國-30天導入Golang系列 第 25

Golang 如何設計一個 buffer pool

今天要進到比較理論的章節,也是比較難解說的部分,如果有什麼疑問或寫錯的地方,歡迎留言指教我。

在這邊大家一定會有疑問? 什麼是 buffer pool ,它有下面優點

  • 減少 garbage collector (GC) 的負擔
  • 可以減少程式不斷像 OS 要記憶體區塊

已使用情境來說,我覺得類似聊天室的應用,需要大量高頻的接收 message ,其實就很適合使用 buffer pool ,畢竟你總不可能有 client 送一個 message 進來 server,就重劃一個記憶體空間給它,處理完就把那塊空間 gc 掉,這樣其實是很吃 CPU 資源的。

下面是一個沒有使用 buffer pool的一個 socket 應用流程圖

no-buffer-pool

如果改成使用 buufer pool 會大概像下面這樣,由圖可以看到,socket client,不像上圖一樣,只要有訊息進來就自己 new 4kb 的空間,改由另一個 buffer pool 去控制,所以當 socket client 需要用時,則向此 buffer pool 取 4kb,用完的時侯,再把 4kb 還回來,反覆利用,避免不斷造成 garbage collector (GC) 問題。

buffer-pool

下面用一個簡單的範例來說明如何實作

type BufferPool struct {
	c chan *bytes.Buffer
}

//預劃 buffer pool 空間
func NewBufferPool(size int) (bp *BufferPool) {
	return &SizedBufferPool{
		c: make(chan *bytes.Buffer, size),
	}
}


//這邊是拿取一個新的 buffer
func (bp *BufferPool) Get() (b *bytes.Buffer) {
	select {
	case b = <-bp.c:
	    //看看 buffer pool 如果有空閑的 buffer 則使用已經建構過的 buffer
	default:
        //如果當下沒有空閑的 buffer 則重新建構一個出來
		b = bytes.NewBuffer(make([]byte, 0, bp.a))
	}
	return
}


//把用完的 buffer 放回 pool 裡面
func (bp *BufferPool) Put(b *bytes.Buffer) {
	b.Reset()


    // 看 buffer pool 空間是否已滿,如果已滿則丟棄掉此 buffer
	select {
	case bp.c <- b:
	default: //放不進去 buffer pool 代表 已滿,則丟掉此 buffer
	}
}

上面範例可以看到,這是運用到 golang select & channel 的特性,把 channel 當作 buffer pool,用 select 當作控制 buufer pool 的閘道。


上一篇
Golang 如何建構一個 websocket server
下一篇
Golang Concurrency Pattern
系列文
Go劍復國-30天導入Golang30

尚未有邦友留言

立即登入留言