go官方限流器
无限流访问
- handler 处理函数
var number int64 //设置一个全局number,记录调用的次数
func handler() {
//函数 sleep 50微秒
atomic.AddInt64(&number, 1)
time.Sleep(time.Millisecond * 50)
}
- call 调用函数
func call() {
for {
handler()
}
}
- main
func main() {
go call()
//handler sleep 50微秒,1秒调用20次,但atomic操作耗时
for i := 0; i < 5; i++ {
time.Sleep(1 * time.Second)
fmt.Println("在一秒中的时间调用了 ", number, " 次")
atomic.StoreInt64(&number, 0)
}
}
- 结果
限流访问
地址:"golang.org/x/time/rate"
- limiter的初始化
//桶的容量为1,每秒放入10个令牌
//两种为同一结果,rate.Every()为放入一个令牌的速率
bucket := rate.NewLimiter(10, 1)
bucket := rate.NewLimiter(rate.Every(100*time.Millisecond), 1)
- 第一种实现方式
桶中没有令牌时会堵塞,等待消耗的令牌数量执行
func call() {
bucket := rate.NewLimiter(rate.Every(100*time.Millisecond), 1)
for {
//第一种实现方式
_ = bucket.WaitN(context.Background(), 1)//与bucket.Wait(context.Background())相同
handler()
}
}
- 第二种实现方式
返回最后的令牌时间与time.Now()时间间隔,有为0,没有为生成令牌数量的时间,使用Delay()获取时间
func call() {
bucket := rate.NewLimiter(rate.Every(100*time.Millisecond), 1)
for {
//第二种实现方式
waitTime := bucket.ReserveN(time.Now(), 1)//与bucket.Reserve()相同
time.Sleep(waitTime.Delay())
}
}
- 第三种实现方式
返回bool类型,是否可以取走相应数量的令牌
func call() {
bucket := rate.NewLimiter(rate.Every(100*time.Millisecond), 1)
for {
if bucket.AllowN(time.Now(), 1) {
handler()
}
}
}
- 结果
作为gin中间件
- middleware
var limitBucket *rate.Limiter //全局limter
func limit() gin.HandlerFunc {
return func(ctx *gin.Context) {
if limitBucket.AllowN(time.Now(), 1) {
ctx.Next()
} else {
ctx.JSON(http.StatusBadRequest, gin.H{
"message": "limit visit",
})
ctx.Abort()
}
}
}
- main
func main() {
//初始化限流器 1 秒放入1个令牌,容量为1
limitBucket = rate.NewLimiter(rate.Every(time.Second*1), 1)
r := gin.Default()
//使用中间件
r.Use(limit())
r.GET("/", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{
"message": "success",
})
})
_ = r.Run(":8080")
}
tips
小生为在校生,本文纯属记录学习内容,若有错误和改进感谢指出,必洗耳恭听。倘若为诸位带来灵感,深感欣慰。