Gin中添加限流中间件

限流概念

高并发系统三大利器“缓存、降级、限流

  • 缓存:提升系统访问速度和增大处理容量,为相应业务增加缓存
  • 降级:当服务器压力剧增时,根据业务策略降级,以释放服务资源保证业务正常
  • 限流:通过对并发限速,以达到拒绝服务、排队或等待、降价等处理

限流策略

漏桶限流:每次请求时计算桶流量,超过阈值则降级请求

有一个漏桶,请求来了就像是往漏桶里注入水,桶满了舍弃请求,但是无论来多少次请求,桶都以固定的速度滴水(处理请求)

因为无论来多少流量是以固定的速度处理请求,所以不能有效的处理大量突发请求的场景

image-20210815095535338

令牌桶限流:每次请求时从令牌桶里取令牌,取不到则降级请

有一个令牌桶,以固定的速率向令牌桶中添加令牌,如果桶满了则丢弃令牌,每次请求来了就向令牌桶中取令牌,取得到就放行处理,令牌不足则拒绝请求

因为令牌桶在平时处理请求时会保持一个桶中余留很多空闲令牌的状态,则可以处理大量突发请求的场景

image-20210815095458830

限流库的使用

安装限流库

1
go get github.com/juju/ratelimit

创建令牌桶的方法

1
2
3
4
5
6
7
8
9
// 创建指定填充速率和容量大小的令牌桶
func NewBucket(fillInterval time.Duration, capacity int64) *Bucket

// 创建指定填充速率、容量大小和每次填充的令牌数的令牌桶
func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket

// 创建填充速度为指定速率和容量大小的令牌桶
// NewBucketWithRate(0.1, 200) 表示每秒填充20个令牌
func NewBucketWithRate(rate float64, capacity int64) *Bucket

取出令牌的方法

1
2
3
4
5
6
7
8
9
10
11
12
// 取token(非阻塞)
// 输入取token的数量,返回时间
func (tb *Bucket) Take(count int64) time.Duration
// 输入去token的数量,返回移除的令牌数,如果取不到token则返回0
func (tb *Bucket) TakeAvailable(count int64) int64

// 最多等maxWait时间取token
func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool)

// 取token(阻塞)
func (tb *Bucket) Wait(count int64)
func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool

Gin中使用限流中间件

在Gin中可以将限流作为中间件注册到路由中

既可以将它注册到全局中间件,也可以将它注册到局部的路由组

1
2
3
4
5
6
7
8
9
10
11
12
13
func RateLimiter(fillTime time.Duration, cap int64) gin.HandlerFunc {
//指定令牌生产速度和容量
bucket := ratelimit.NewBucket(fillTime, cap)
return func(c *gin.Context) {
// 取不到令牌则返回0
if bucket.TakeAvailable(1) < 1 {
c.JSON(http.StatusOK, "rate limit...")
c.Abort()
return
}
c.Next()
}
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!