1. 源码阅读

  • 整个包实现原理基于令牌桶算法:随时间以 1/r 个令牌的速度向容积为 b 个令牌的桶中添加令牌,有请求就取走令牌,若令牌不足则不执行请求或者等待
  • Allow 方法的调用链:lim.Allow() bool → lim.AllowN(time.Now(), 1) → lim.reserveN(now, n, 0).ok,因此 reserveN 方法的实现很关键
// Allow is shorthand for AllowN(time.Now(), 1).
func (lim *Limiter) Allow() bool {
return lim.AllowN(time.Now(), )
} // AllowN reports whether n events may happen at time now.
// Use this method if you intend to drop / skip events that exceed the rate limit.
// Otherwise use Reserve or Wait.
func (lim *Limiter) AllowN(now time.Time, n int) bool {
return lim.reserveN(now, n, ).ok
}
  • reserveN 方法是线程安全的,通过互斥锁锁住判断操作:

lim.mu.Lock()
......
lim.mu.Unlock()
return r
  • reserveN 方法,首先检查限制为 Inf,不需要判断直接返回 true:

if lim.limit == Inf {
lim.mu.Unlock()
return Reservation{
ok: true,
lim: lim,
tokens: n,
timeToAct: now,
}
}
  • 时间和令牌的计算是根据单位时间内的限制数量和剩余令牌数所占比例计算的:
// durationFromTokens is a unit conversion function from the number of tokens to the duration
// of time it takes to accumulate them at a rate of limit tokens per second.
func (limit Limit) durationFromTokens(tokens float64) time.Duration {
seconds := tokens / float64(limit)
return time.Nanosecond * time.Duration(1e9*seconds)
} // tokensFromDuration is a unit conversion function from a time duration to the number of tokens
// which could be accumulated during that duration at a rate of limit tokens per second.
func (limit Limit) tokensFromDuration(d time.Duration) float64 {
return d.Seconds() * float64(limit)
}
  • 根据令牌数限制和当前存在的令牌数计算还有上次更新至今的时间差计算可以补充多少令牌:
// Avoid making delta overflow below when last is very old.
maxElapsed := lim.limit.durationFromTokens(float64(lim.burst) - lim.tokens)
elapsed := now.Sub(last)
if elapsed > maxElapsed {
elapsed = maxElapsed
} // Calculate the new number of tokens, due to time that passed.
delta := lim.limit.tokensFromDuration(elapsed)
tokens := lim.tokens + delta
if burst := float64(lim.burst); tokens > burst {
tokens = burst
}
  • 更新补充后的当前令牌数减去请求的令牌数,如果不足,根据不足的令牌数计算需要等待的时间:
// Calculate the remaining number of tokens resulting from the request.
tokens -= float64(n)
fmt.Printf("WangAo test: After sub n: tokens: %v\n", tokens) // Calculate the wait duration
var waitDuration time.Duration
if tokens < {
waitDuration = lim.limit.durationFromTokens(-tokens)
}
  • 根据请求数量和等待时间判断是否允许请求:
// Decide result
ok := n <= lim.burst && waitDuration <= maxFutureReserve

3. 参考文献

Golang(七)golang.org/x/time/rate 实现频率限制的更多相关文章

  1. 【GoLang】GoLang map 非线程安全 & 并发度写优化

    Catena (时序存储引擎)中有一个函数的实现备受争议,它从 map 中根据指定的 name 获取一个 metricSource.每一次插入操作都会至少调用一次这个函数,现实场景中该函数调用更是频繁 ...

  2. 【GoLang】golang HTTP GET/POST JSON的服务端、客户端示例,包含序列化、反序列化

    服务端代码示例: package main import ( "encoding/json" "fmt" "io/ioutil" " ...

  3. 【GoLang】golang垃圾回收 & 性能调优

    golang垃圾回收 & 性能调优 参考资料: 如何监控 golang 程序的垃圾回收_Go语言_第七城市 golang的垃圾回收(GC)机制 - 两只羊的博客 - 博客频道 - CSDN.N ...

  4. 【GoLang】golang 最佳实践汇总

    最佳实践 1 包管理 1.1 使用包管理对Golang项目进行管理,如:godep/vendor等工具 1.2 main/init函数使用,init函数参考python 1.2.1 main-> ...

  5. 【GoLang】golang 的精髓--流水线,对现实世界的完美模拟

    直接上代码: package main import ( "fmt" "runtime" "strconv" "sync" ...

  6. 【GoLang】golang 中 defer 参数的蹊跷

    参考资料: http://studygolang.com/articles/7994--Defer函数调用参数的求值 golang的闭包和普通函数调用区别:http://studygolang.com ...

  7. 【GoLang】golang context channel 详解

    代码示例: package main import ( "fmt" "time" "golang.org/x/net/context" ) ...

  8. 【GoLang】golang 交叉编译 实现&工具

    apt-get install gcc-mingw-w64 env CGO_ENABLED= GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc g ...

  9. 【GoLang】golang底层数据类型实现原理

    虽然golang是用C实现的,并且被称为下一代的C语言,但是golang跟C的差别还是很大的.它定义了一套很丰富的数据类型及数据结构,这些类型和结构或者是直接映射为C的数据类型,或者是用C struc ...

随机推荐

  1. EFCore 2.0的IEntityTypeConfiguration<TEntity>的使用!

    通过新建一个类来实现  IEntityTypeConfiguration 这个接口,将EFCore中的实体配置写在单独的配置类中,便于修改和维护. OnModelCreating代码: protect ...

  2. Linux之《荒岛余生》(三)内存篇

    原文:https://juejin.im/post/5c00aee06fb9a049be5d3641 小公司请求量小,但喜欢滥用内存,开一堆线程,大把大把往jvm塞对象,最终问题是内存溢出. 大公司并 ...

  3. Mac 下安装 jdk

    1.安装jdk 我们是需要java环境的- 到oracle官网下载se: Java SE Development Kit 8 Downloads https://www.oracle.com/tech ...

  4. js执行上下文栈和变量对象

    JavaScript执行上下文栈和变量对象 JS是单线程的语言,执行顺序肯定是顺序执行,但是JS 引擎并不是一行一行地分析和执行程序,而是一段一段地分析执行,会先进行编译阶段然后才是执行阶段. 例子一 ...

  5. 路由拨号上网过Drcom

    学校校园宽带是Drcom认证的 ,一人一号一设备.用着难受就决定想破解. 开始想着用软路由,但是感觉对电脑不友好,所以就决定买个路由器来搞. 一丶环境说明 学校使用的是Drcom 6.0 P版客户端. ...

  6. QTableWidget表格属性

    ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); //设置不可编辑 ui->tableWidg ...

  7. 有些CRM settype用事务码COMM_ATTRSET打不开的原因

    This question is asked by Dr. Lin. Issue For example, settype COM_COMMERCIAL could be opened via tco ...

  8. 06. redis cluster

    目录 Redis Cluster redis cluster 特点 搭建redis cluster 访问redis cluster redis-cli 访问redis cluster 重新分片数据 新 ...

  9. 让天堂的归天堂,让尘土的归尘土——谈Linux的总线、设备、驱动模型

    本文系转载,著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 作者: 宋宝华 来源: 微信公众号linux阅码场(id: linuxdev) 公元1951年5月15日的国会听证上, ...

  10. pymysql 增删改 查 索引

    pymysql 模块的使用 pip install pymysql username = input ("请输入用户") pwd = input ("请输入密码" ...