14.15 漏桶算法

(译者注:翻译遵照原文,但是对于完全没听过这个算法的人来说比较晦涩,请配合代码片段理解)

考虑以下的客户端-服务器结构:客户端协程执行一个无限循环从某个源头(也许是网络)接收数据;数据读取到 Buffer 类型的缓冲区。为了避免分配过多的缓冲区以及释放缓冲区,它保留了一份空闲缓冲区列表,并且使用一个缓冲通道来表示这个列表:var freeList = make(chan *Buffer,100)

这个可重用的缓冲区队列 (freeList) 与服务器是共享的。 当接收数据时,客户端尝试从 freeList 获取缓冲区;但如果此时通道为空,则会分配新的缓冲区。一旦消息被加载后,它将被发送到服务器上的 serverChan 通道:

  1. var serverChan = make(chan *Buffer)

以下是客户端的算法代码:

  1. func client() {
  2. for {
  3. var b *Buffer
  4. // Grab a buffer if available; allocate if not
  5. select {
  6. case b = <-freeList:
  7. // Got one; nothing more to do
  8. default:
  9. // None free, so allocate a new one
  10. b = new(Buffer)
  11. }
  12. loadInto(b) // Read next message from the network
  13. serverChan <- b // Send to server
  14. }
  15. }

服务器的循环则接收每一条来自客户端的消息并处理它,之后尝试将缓冲返回给共享的空闲缓冲区:

  1. func server() {
  2. for {
  3. b := <-serverChan // Wait for work.
  4. process(b)
  5. // Reuse buffer if there's room.
  6. select {
  7. case freeList <- b:
  8. // Reuse buffer if free slot on freeList; nothing more to do
  9. default:
  10. // Free list full, just carry on: the buffer is 'dropped'
  11. }
  12. }
  13. }

但是这种方法在 freeList 通道已满的时候是行不通的,因为无法放入空闲 freeList 通道的缓冲区会被“丢到地上”由垃圾收集器回收(故名:漏桶算法)。

链接