前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >golang--ratelimit令牌桶原理分析

golang--ratelimit令牌桶原理分析

原创
作者头像
小许code
发布2023-02-21 09:44:58
5650
发布2023-02-21 09:44:58
举报
文章被收录于专栏:小许code小许code

什么是限流

限流对某一时间窗口内高于系统承载的请求进行限制,通过限速来保护系统,一旦达到限制速率则可拒绝服务,等待。常见调用平台及服务,比如微信发消费券服务每秒500qps,万一我们超过请求频次,就会发生意想不到的业务问题,踩过坑的小伙伴深有体会

限流方式-令牌桶

常见限流方法:计数器、令牌桶、漏桶。这里我们就只展开对令牌桶展开讨论。令牌桶是以一定的速率往令牌桶中生产令牌,桶满则丢弃,请求request过来时,先从桶中获取一个令牌,成功获取令牌就处理请求,失败就丢弃请求

令牌桶实现原理

  1. 单位时间按照一定速率匀速的生产 token放入桶内,直到达到桶容量上限
  2. 处理请求,每次尝试获取一个或多个令牌
  3. 如果拿到则处理请求,失败则拒绝请求

juju/ratelimit令牌桶限流器在golang开发中使用比较多,而且自己在项目中刚好需要使用到、今天就这个限流进行了解,学习使用和实现的原理

juju/ratelimit中主要有三个文件,ratelimit.go, ratelimit_test.go、reader.go

先看ratelimit定义令牌桶的结构Bucket,简单看Bucket中属性的值代表的意义

代码语言:javascript
复制
type Bucket struct {
      //Clock是个接口,由realClock根据标准时间函数实现
    clock Clock

    //令牌桶首次创建的时间
    startTime time.Time

    //桶容量
    capacity int64

    //每个tick向桶内添加的令牌数
    quantum int64

    //每个刻度的间隔
    fillInterval time.Duration

    //锁
    mu sync.Mutex

    //相关的tick有效令牌数,消费者等待令牌时为负数
    availableTokens int64

    //令牌中的数量保持最新的刻度
    latestTick int64
}

重点:tick计算方式

(当前时间 - 初始时间)/ 时间间隔(fillInterval )

代码语言:javascript
复制
func (tb *Bucket) currentTick(now time.Time) int64 {
   return int64(now.Sub(tb.startTime) / tb.fillInterval)
}

创建令牌桶的方式

代码语言:javascript
复制
// 指定时间间隔、容量大小的令牌桶、每个tick添加的令牌数 1、 clock的类型是Clock
func NewBucket(fillInterval time.Duration, capacity int64) *Bucket
// 指定时间间隔、容量大小、每个tick添加的令牌数 quantum 
func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket
// 创建填充速度为指定速率和容量大小的令牌桶
// NewBucketWithRate(0.5, 100) 表示每秒填充50个令牌, fillInterval 时间间隔为1
func NewBucketWithRate(rate float64, capacity int64) *Bucket

其实这三种方式调用的都是下面函数的二次封装

代码语言:javascript
复制
func NewBucketWithQuantumAndClock(fillInterval time.Duration, capacity, quantum int64, clock Clock) *Bucket

领取令牌

下面到了获取令牌的时候了TakeAvailable() 获取令牌,获取令牌会使用sync.Mutx 进行加锁,执行完后解锁。

获取令牌时,调整令牌桶的的代码如下

代码语言:javascript
复制
func (tb *Bucket) adjustavailableTokens(tick int64) {
   if tb.availableTokens >= tb.capacity {
      return
   }
   tb.availableTokens += (tick - tb.latestTick) * tb.quantum
   if tb.availableTokens > tb.capacity {
      tb.availableTokens = tb.capacity
   }
   tb.latestTick = tick
   return
}
  1. 如果当前可用令牌数大于等于当前容量,就不更新可用令牌数量
  2. tb.availableTokens += (tick - tb.latestTick) * tb.quantum 可用令牌数 = 可用令牌数 + (当前时间的tick - 最后更新的tick) * 每个tick令牌添加数
  3. 如果availableTokens 大于容量,因为不能超过容量,所以可用令牌数设置为容量值
  4. 更新latestTick的值
  5. 获取令牌的方式也是将总数 -1

上面更新令牌数的方式没有通过其他的goroutine等方式去更新,而是转换为了挺有意思的数学问题,简单明了直接。当然juju/ratelimit还有其他一些获取令牌桶信息的方法,这里就不展开讨论了。具体我们可以去看源码,整体代码量还是可以接受的。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是限流
  • 限流方式-令牌桶
  • 重点:tick计算方式
  • 创建令牌桶的方式
  • 领取令牌
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档