码迷,mamicode.com
首页 > 其他好文 > 详细

Golang令牌桶-频率限制

时间:2016-05-22 13:52:19      阅读:266      评论:0      收藏:0      [点我收藏+]

标签:

令牌桶算法

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

  我们的具体需求是对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令牌桶-频率限制

标签:

原文地址:http://www.cnblogs.com/golangguo/p/5516571.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!