令牌桶算法
令牌桶算法一般用做频率限制、流量限制等,可能具体有单速双色、单速三色、双速三色等方法。
我们的具体需求是对API的调用的频率做限制,因此实现的是单速双色。
package main
import (
"errors"
"fmt"
"strconv"
"sync"
"time"
)
const TOKEN_GRANULARITY = 1000
type MAP struct {
lock sync.RWMutex
bucket map[string]*tokenBucket
}
//是否可实现一种无锁算法 TODO
func (m *MAP) Set(k string, times, interval int) {
m.lock.Lock()
defer m.lock.Unlock()
if _, ok := m.bucket[k]; !ok {
tb := new(tokenBucket)
tb.Init(times, interval)
m.bucket[k] = tb
}
return
}
func Atoi(s string) int {
i, _ := strconv.Atoi(s)
return i
}
func NewMAP() *MAP {
return &MAP{bucket: make(map[string]*tokenBucket)}
}
type tokenBucket struct {
lastQuery time.Time
tokens int
burst int
step int
add int
mu sync.Mutex
}
func (tb *tokenBucket) Init(quota int, interval int) {
tb.burst = quota * TOKEN_GRANULARITY //最多保留的令牌
tb.tokens = quota * TOKEN_GRANULARITY //当前保留的令牌
tb.step = quota * TOKEN_GRANULARITY / quota //每次需消耗的令牌
tb.add = quota * TOKEN_GRANULARITY * interval / interval //每秒增加的令牌
tb.lastQuery = time.Now()
}
func (tb *tokenBucket) TokenBucketQuery() error {
now := time.Now()
diff := now.Sub(tb.lastQuery)
token := int(diff.Nanoseconds()/1000000000) * tb.add
tb.mu.Lock()
defer tb.mu.Unlock()
if token != 0 {
tb.lastQuery = now
tb.tokens += token //增加此段时间的令牌
}
if tb.tokens > tb.burst { //超过最大令牌数重置
tb.tokens = tb.burst
}
if tb.tokens >= tb.step { // 与每次消耗的做对比
tb.tokens -= tb.step
return nil
}
return errors.New("Not enough")
}
//@test
func main() {
var tb tokenBucket
tb.Init(5, 1) //1s内调用5次
cnt := 0
for {
err := tb.TokenBucketQuery()
if err != nil {
fmt.Println(err)
} else {
fmt.Println("take")
}
cnt += 1
fmt.Println(cnt)
time.Sleep(100000 * time.Microsecond)
//time.Sleep(5* time.Second)
}
}
测试结果:

现在的实现是需要锁来保证多线程安全,不知道有没有一种无锁的实现,有待研究