跳到主要内容

Cvgo 提供了一个简单的令牌桶限流器,匀速向桶中添加令牌,服务请求时从桶中获取令牌,根据是否获取到了令牌来决定是否执行后续逻辑。 相关文献

使用示例

限制 100 TPS 为例

package main

import (
"fmt"
"cvgo/kit/gokit"
"time"
)

func main() {
// 填充令牌
var restrictor = gokit.NewTokenBucket(time.Millisecond*10, 100)

// 填充令牌是异步的,睡眠一会便能获取到令牌
time.Sleep(time.Second * 1)

// 取令牌
token := restrictor.TakeToken()
if token {
fmt.Println("获得令牌")
} else {
fmt.Println("没有令牌,请稍后再试")
}
}

把允许的最大访问比做一个桶,匀速向桶中添加令牌,服务请求时需要从桶中获取令牌,如果没有令牌不执行业务逻辑,可以选择等待,或者放弃。

填充令牌:gokit.NewTokenBucket(fillInterval time.Duration, cap int)

  • fillInterval: 每隔多久往桶中填充一个令牌
  • cap: 桶容量

例如gokit.NewTokenBucket(time.Millisecond*10, 100) 就是每 10ms 填充一个令牌,1 秒刚好放进来 100 个,也就是 100 TPS

获取令牌:gokit.TakeToken(await ...bool)

  • await: 获取不到令牌时的行为。传 true 时如果桶中没有令牌则会阻塞线程,等待桶中有令牌的时候才解除阻塞,即把流量挡着等有空位一个放一个进来。 传 false 时不会阻塞等待,直接返回现在有没有令牌,即服务熔断。

最佳实践

设置限流器

你可以在程序启动时调用 Restrictor = gokit.NewTokenBucket() 填充令牌,一般我会创建一个 boot 包,这个包下面专门写一些程序启动时的初始化逻辑。 然后将 boot.Restrictor 作为全局变量。

对业务限流

在开发 Web 服务时你可以对单个接口调用 gokit.TakeToken() 来实现限流。也可以结合 自定义中间件 来对一组路由或全局路由(整个服务)进行限流。