设计模式 - 工厂模式
React - Tic Tac Toc

Go Channel Practice IV - Token Bucket

violet posted @ 5 年前 in 读书笔记 with tags Golang GoRoutine , 254 阅读

https://en.wikipedia.org/wiki/Token_bucket

 

先定义 TokenBucket

1
2
3
4
5
6
7
8
type TokenBucket struct {
    Depth  int
    Tokens int
    Delta  int
    ticker *time.Ticker
    mutex  sync.Mutex
    quit   chan bool
}

Depth 是 bucket 的总深度,Tokens 是当前可以用到的数目,Delta 是每秒向 bucket 增加的 token 数目。ticker, mutex 和 quit 类似 Leaky Bucket 的用法。

NewTokenBucket 创建一个 goroutine,不断在每个 tick 里向 token 里加 delta 个 token。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func NewTokenBucket(depth, delta int) *TokenBucket {
    ticker := time.NewTicker(time.Second)
    tb := &TokenBucket{
        Depth:  depth,
        Tokens: depth,
        Delta:  delta,
        ticker: ticker,
        mutex:  sync.Mutex{},
        quit:   make(chan bool),
    }
 
    go func() {
        for {
            select {
            case <-ticker.C:
                fmt.Println("tick...")
                tb.mutex.Lock()
                if tb.Tokens+tb.Delta > tb.Depth {
                    tb.Tokens = tb.Depth
                } else {
                    tb.Tokens += tb.Delta
                }
                tb.mutex.Unlock()
            case <-tb.quit:
                return
            }
        }
    }()
 
    return tb
}

ticker 和 goroutine 也需要 close

1
2
3
4
func (t *TokenBucket) Close() {
    t.ticker.Stop()
    t.quit <- true
}

创建一个 token bucket 之后调用,判断每次取的 token 是不是大于当前 bucket 内的 token,如果大于,则失败,否则成功。

1
2
3
4
5
6
7
8
9
10
11
func (t *TokenBucket) Withdraw(count int) bool {
    t.mutex.Lock()
    defer t.mutex.Unlock()
    if count > t.Tokens {
        fmt.Println("failed to withdraw")
        return false
    }
    t.Tokens -= count
    fmt.Println("withdraw succeeded")
    return true
}

完整测试如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package main
 
import (
    "fmt"
    "sync"
    "time"
)
 
func main() {
    tb := NewTokenBucket(1000, 300)
    for i := 0; i < 300; i++ {
        if !tb.Withdraw(100) {
            time.Sleep(time.Millisecond * 100)
        }
    }
}
 
type TokenBucket struct {
    Depth  int
    Tokens int
    Delta  int
    ticker *time.Ticker
    mutex  sync.Mutex
    quit   chan bool
}
 
func NewTokenBucket(depth, delta int) *TokenBucket {
    ticker := time.NewTicker(time.Second)
    tb := &TokenBucket{
        Depth:  depth,
        Tokens: depth,
        Delta:  delta,
        ticker: ticker,
        mutex:  sync.Mutex{},
        quit:   make(chan bool),
    }
 
    go func() {
        for {
            select {
            case <-ticker.C:
                fmt.Println("tick...")
                tb.mutex.Lock()
                fmt.Println(tb.Tokens)
                if tb.Tokens+tb.Delta > tb.Depth {
                    tb.Tokens = tb.Depth
                } else {
                    tb.Tokens += tb.Delta
                }
                fmt.Println(tb.Tokens)
                tb.mutex.Unlock()
            case <-tb.quit:
                return
            }
        }
    }()
 
    return tb
}
 
func (t *TokenBucket) Close() {
    t.ticker.Stop()
    t.quit <- true
}
 
func (t *TokenBucket) Withdraw(count int) bool {
    t.mutex.Lock()
    defer t.mutex.Unlock()
    if count > t.Tokens {
        fmt.Println("failed to withdraw")
        return false
    }
    t.Tokens -= count
    fmt.Println("withdraw succeeded")
    return true
}
charlly 说:
2 年前

The token bucket algorithm is based on the idea of a defined capacity bucket into which tokens, typically denoting a unit of bytes or a single packet of a specific size, are introduced at a fixed rate. The bucket is examined to see if it currently has Lab grown diamonds enough tokens when a packet needs to be validated for conformity to the stated restrictions.


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter