一份golang令牌桶使用攻略(juju/ratelimit)

使用场景

令牌桶的一个主要使用场景是限流。
程序以一定的速率生产令牌加入到令牌桶中。
每个请求到达时都会尝试从令牌桶中获取一块令牌, 如果获取令牌失败(令牌桶为空)则不处理该请求, 以此达到限流的目的。

juju/ratelimit使用

juju/ratelimit是开源的、golang语言实现的高效令牌桶,代码简洁,在本文写作时该项目有2.4k的start。
项目地址是 github.com/juju/ratelimit

api介绍

创建令牌锁

先看三个创建令牌锁的方法及它们的区别:

  1. NewBucket
    创建一个令牌桶, 设置填充频率(fillInterval)和初始容量(capacity), 每填充频率的时间会向令牌桶中加入1块令牌。
    func NewBucket(fillInterval time.Duration, capacity int64) *Bucket
//令牌桶容量为1,每10ms填充一块令牌
bucket := ratelimit.NewBucket(10 * time.Millisecond, 1)
  1. NewBucketWithQuantum
    创建一个令牌桶, 设置填充频率(fillInterval)、初始容量(capacity)、每秒填充的令牌数(quantum), 每填充频率的时间会向令牌桶中加入quantum块令牌。
    func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket
//令牌桶容量为3, 每10ms填充3块令牌
bucket := ratelimit.NewBucketWithQuantum(10 * time.Millisecond, 3, 3)
  1. NewBucketWithRate
    创建一个令牌桶, 设置每秒速率(rate)、初始容量(capacity)。
    func NewBucketWithRate(rate float64, capacity int64) *Bucket
//令牌桶容量为1, 每秒限速100次
bucket := ratelimit.NewBucketWithRate(100, 1)

获取令牌

  1. Take 非阻塞, 返回需等待的时间
    func (tb *Bucket) Take(count int64) time.Duration

  2. TakeAvailable 非阻塞, 令牌数不满足需求时, 返回可用的令牌数
    func (tb *Bucket) TakeAvailable(count int64) int64

  3. TakeMaxDuration 非阻塞, 返回需等待时间, 超过最大时间返回 0, false
    func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool)

  4. Wait 阻塞, 直到拿到令牌
    func (tb *Bucket) Wait(count int64)

  5. WaitMaxDuration 阻塞 若在最大等待时间内能拿到令牌则阻塞, 否则立即返回false
    func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool

其它方法

  1. Available 返回当前可用令牌数
    func (tb *Bucket) Available() int64

  2. Rate 返回每秒限流速率
    func (tb *Bucket) Rate() float64

关键源码分析

令牌桶结构是怎样的

type Bucket struct {//Clock提供了获取当前时间、sleep指定时间的方法clock Clock//桶被创建的时间, 当前属于第几个tick也是基于这个起始时间来计算startTime time.Time// 桶容量capacity int64// 每个tick向桶填充的令牌数quantum int64// 填充间隔fillInterval time.Duration// 桶方法使用的互斥锁, 保障线程安全 mu sync.Mutex// 桶内可用的令牌数// 当有操作等待令牌的时候, 这个数值会变成负数availableTokens int64// 上一个填充tick// 计算当前tick与上一个tick的差值 得出需要填充的令牌数latestTick int64
}

按一定的速率向桶中填充令牌是如何实现的?

juju/ratelimit没有启用额外的线程定时向桶中填充令牌, 而是在外部调用令牌锁的方法时触发一次填充方法,根据当前时间和令牌锁的创建时间差值计算出是否需要填充、需要填充的数量。

填充令牌的方法, 外部调用令牌锁的方法时会触发

func (tb *Bucket) adjustavailableTokens(tick int64) {//令牌桶结构中记录了上一个填充周期的值lastTick := tb.latestTicktb.latestTick = tick//如果桶是满的直接返回if tb.availableTokens >= tb.capacity {return}//需要填充的数量是 (本周期数 - 上次填充周期数) * 单周期填充数tb.availableTokens += (tick - lastTick) * tb.quantum//填充数量不得超过桶的容量if tb.availableTokens > tb.capacity {tb.availableTokens = tb.capacity}return
}

获取令牌的关键代码

Take(), TakeMaxDuration(), Wait(), WaitMaxDuration()这几个方法都是通过调用take()这个内部方法实现的

func (tb *Bucket) take(now time.Time, count int64, maxWait time.Duration) (time.Duration, bool) {if count <= 0 {return 0, true}//计算当前tick, 调用adjustavailableTokens填充令牌tick := tb.currentTick(now)tb.adjustavailableTokens(tick)//用可用令牌数(availableTokens)减去需要获取的令牌数(count),这里计算出的avail可能为负值avail := tb.availableTokens - count//令牌充足, 返回0(不需要等待), true(获取令牌成功)if avail >= 0 {tb.availableTokens = availreturn 0, true}//计算出一个endTick, 在未来的endTick到达时,令牌数将不再是负的endTick := tick + (-avail+tb.quantum-1)/tb.quantum//计算endTick的时间点endTime := tb.startTime.Add(time.Duration(endTick) * tb.fillInterval)//需要等待的时间时endTime - nowwaitTime := endTime.Sub(now)if waitTime > maxWait {return 0, false}//更新availableTokens, 可能为负值tb.availableTokens = avail//返回等待时间, 获取成功return waitTime, true
}

使用令牌锁的简单例子

代码:

import ("github.com/juju/ratelimit""time"
)func main() {//创建一个令牌桶初始容量为1, 每10ms填充3个令牌bucket := ratelimit.NewBucketWithQuantum(10 * time.Millisecond, 1, 3)//程序运行长3秒endTime := time.Now().Add(3 * time.Second)//打印桶的每秒限速频率(预期300/s)println("bucket rate:" , bucket.Rate(), "/s")//使用一个变量记录获取令牌的总数var tockensCount int64 = 0for {//每次拿1块令牌, 成功返回1, 失败返回0tocken := bucket.TakeAvailable(1)tockensCount += tockenif(time.Now().After(endTime)) {println("tockensCount: ", tockensCount)return;}time.Sleep(5 * time.Millisecond)}}

程序运行结果:

bucket rate: +3.000000e+002 /s
tockensCount:  301

tockensCount为什么是301而不是300:
因为创建的令牌桶初始容量为1。桶初始化完成后里面已经有一块令牌了, 可以立即拿到这块令牌不需要等待填充。
在后面的3000ms共填充了300块令牌。

使用令牌桶时要注意, 由于令牌桶是有"容量"的, 允许一定的瞬时流量, 对限制速率有严格要求的时候要小心设置容量填充速度, 并进行实测验证。

一份golang令牌桶攻略(juju/ratelimit)相关推荐

  1. 这份公众号运营攻略,可以帮你系统地运营好公众号

    小编看网上说:公众号红利期已经成为过去了,现在的公众号已经越来越难做了. 后半句我是赞同的,公众号运营是一门学问,不是随随便便就能做好的: 但是公众号红利期一直存在,公众号可以帮助企业和品牌提高自身知 ...

  2. python爬取去哪儿网机票_干货|Python爬取《去哪儿》攻略库,制作一份详细的旅行攻略,疫情后来一场说走就走的旅行!...

    去哪儿是中国领先的在线OTA网站,为消费者提供机票.酒店.会场 .度假产品的实时搜索,并提供旅游产品团购以及其他旅游信息服务.去哪儿网站上有丰富的图片.评论数据,这些大量的数据对于从事数据岗位的来说的 ...

  3. 福昕阅读器:你有一份七夕土味情话攻略待查收

    福昕阅读器:你有一份七夕土味情话攻略待查收 呵,七夕又来了. 好的吧,可是这跟既没有汪星人可以溜,也没有喵星人可以撸,更没有男盆友可以撩的我有什么关系呢? 哦!有关系~~ 这不,一大波狗粮is com ...

  4. 2021成都高考二诊成绩查询,2021成都二诊出分在即!这份全省位次换算攻略,送给你!...

    原标题:2021成都二诊出分在即!这份全省位次换算攻略,送给你! 高三一年,会经历大小不断的各类考试,从周考.月考.二诊...顾名思义诊断考试,重在诊断. 大型考试,锦宏教育都会早早收集.推送试题与答 ...

  5. 万字长文,小学弟熬夜肝了这份腾讯面试攻略

    本文一万字,以非腾讯在职人的身份,来聊腾讯面试的流程.攻略和建议,但愿能助有缘人. 标题涉及的范围很广,对多数面试腾讯的人而言,都有参考价值,看完本文之后,必有所得. 退一步来讲,即便是准备面试其他公 ...

  6. 字节程序媛:大厂技术岗求职流程解读经验分享,这是一份保姆级校招攻略

    文章目录 写在前面 流程解读 简历投递 笔试(仅校招) 面试 发Offer 写在最后 写在前面 阳春三月,春暖花开.更重要的是- 一年一度的春招季他来啦!作为校招的两大关键节点之一,春招是应届生去争取 ...

  7. 实验楼勋章、成就系统上线!这里有一份完整的收集攻略

    对于程序员来说 最能体现编程功力的东西是什么? 头发?履历?Github? "不,还有实验楼的勋章!" 实验楼 勋章.成就系统 正式上线 一共 13 个勋章,如何获得? 我们为大家 ...

  8. 程序员的职业规划_从菜鸡到大佬——程序员们,请收下这份职业规划全攻略!...

    作者:阿诺,有删改 引言 John Z. Sonmez是一位来自硅谷的杰出程序员,2016年他出版了<软技能:代码之外的生存指南>一书.这本书在中国翻译出版之后,引起了国内广大程序员的热烈 ...

  9. mysql 修改字段注释_MySQL数据库+命令大全,人手一份的实操攻略来啦

    有很多朋友虽然安装好了MySQL但却不知如何使用它.在这篇文章中我们就从连接MySQL.修改密码.增加用户等方面来学习一些MySQL的常用命令,再看看MySQL在数据库方面的操作是如何执行的. 连接M ...

最新文章

  1. Tokyo Cabinet 安装
  2. nginx 缓存时间说明
  3. eventEmitter3源码分析与学习
  4. 卡通渲染进阶 = toonlighting + outline + rimlighting + hair specular
  5. 简单复读机LR如何成为推荐系统精排之锋?
  6. 【开发者成长】5 分钟搞定 Linux 正则表达式
  7. linux总线驱动程序,Linux驱动程序中的platform总线详解
  8. Codeforces 1194B+1194D
  9. 解决RuntimeError: stack expects a non-empty Tensorlist问题
  10. qpython androidhelper gps_Qpython SL4A获取手机电量和GPS
  11. 【VOLTE案例解析】南京移动拨打10086回落到2G,拨打其他电话正常使用VOLTE
  12. 如何提高团队管理能力
  13. 新员工犯错续:解决问题
  14. MBA-day29 算术-绝对值初步认识
  15. 亚商投顾早餐FM/0905北交所首只指数、两融制度要来了
  16. ppt转html java_poi ppt转换为html,实现在线预览
  17. mysql 5.6 不同步_MySQL5.6配置同步复制的新方法以及常见问题的解决方法
  18. IDEA代码push到github报错 fatal:unable to access...
  19. Lab VRF Rip
  20. NeRF:用深度学习完成3D渲染任务的蹿红

热门文章

  1. MySQL事务的4种隔离级别
  2. 用MATLAB画圣诞树的源代码
  3. 关于dialogbox
  4. Star ccm+ 算例演示
  5. 1天熟记----数据库
  6. 论文阅读《Direct Sparse Odometry》1
  7. 条件生成对抗神经网络,生成对抗网络gan原理
  8. 自定义view----六边形战斗力图表
  9. 一文读懂ZigBee无线通信技术
  10. 王者服务器维护公告2月,王者荣耀2月22日体验服停机更新公告 英雄调整