Golang令牌桶-频率限制

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang令牌桶-频率限制相关的知识,希望对你有一定的参考价值。

令牌桶算法

  令牌桶算法一般用做频率限制、流量限制等,可能具体有单速双色、单速三色、双速三色等方法。

  我们的具体需求是对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)
        }
}

  测试结果:

    技术分享

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

以上是关于Golang令牌桶-频率限制的主要内容,如果未能解决你的问题,请参考以下文章

使用 Golang 实现简易的令牌桶算法

使用 Golang 实现简易的令牌桶算法

使用 Golang 实现简易的令牌桶算法

限流算法之漏桶算法令牌桶算法

使用golang实现令牌桶限流和时间窗口控制

使用golang实现令牌桶限流和时间窗口控制