• 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)
            }
    }
    

      测试结果:

        

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

  • 相关阅读:
    原理Lambda表达式
    关于Web.config的debug和release.config文件
    利用GeoIP数据库及API进行地理定位查询 Java
    JSONP跨域的原理解析
    如何处理JSON中的特殊字符
    IIS下防止mdb数据库被下载的实现方法
    点击文字选中radio
    关于微信40029错误
    nopcommerce的WidgetZones
    js判断移动终端(手机浏览器)
  • 原文地址:https://www.cnblogs.com/golangguo/p/5516571.html
Copyright © 2020-2023  润新知