golang使用官方限流器,并简单结合gin框架

265 阅读1分钟

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)
	}
}
  • 结果

unlimit.png

限流访问

地址:"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()
		}
	}
}
  • 结果

limit1.png

作为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

小生为在校生,本文纯属记录学习内容,若有错误和改进感谢指出,必洗耳恭听。倘若为诸位带来灵感,深感欣慰。