zoukankan      html  css  js  c++  java
  • Golang 服务流量限流器

    1、简介

    限流器是后台服务中的非常重要的组件,可以用来限制请求速率,保护服务,以免服务过载。 限流器的实现方法有很多种,例如滑动窗口法、Token Bucket、Leaky Bucket等。

    golang标准库中就自带了限流算法的实现,即golang.org/x/time/rate。 该限流器是基于Token Bucket(令牌桶)实现的。

    简单来说、令牌桶就是想象一个固定大小的桶、系统会以为恒定速率向桶中存放Token,桶满则暂时不放。

    而用户则从桶中获取Token,存在Token就会一直获取,如果没有Token,则需要等待系统中被放置Token才行

    2、使用

    2.1、NewLimiter 创建限流器对象
    limiter := NewLimiter(10,1)
    

    两个参数:

    • 第一参数r Limit:代表每秒可以向Token桶中存放多少Token,Limit类型实际上是float64
    • 第二参数b int:b代表Token桶容量大小。

    其构造出的限流器含义是令牌桶大小为1,以每秒10个Token的速率想桶中存放Token

    2.2、Every 设置存放间隔时间
    limit := Every(100 * time.Millisecond)
    limiter := NewLimiter(limit,1)
    

    上述表示以每100ms向桶中存放一个Token,本质上就是1s存放10个Token

    2.3、Wait/WaitN
    func (lim *Limiter) Wait(ctx context.Context) (err error)
    func (lim *Limiter) WaitN(ctx context.Context,n int) (err error)
    

    Wait实际上就是WaitN(ctx,1)

    当Wait方法消费Token时,此时桶内Token不足(小于N),Wait就会阻塞一段时间,直到Token满足条件。如果充足则直接返回。

    Wait方法有context参数,可通过context的Deadline或Timeout,设置Wait等待时长。

    2.4、Allow/AllowN
    func (lim *Limiter) Allow() bool
    func (lim *Limiter) AllowN(now time.Time,n int) bool
    

    Allow实际上就是AllowN(time.Now(),1)

    AllowN表示截止到某一时刻,目前桶中数据是否至少为n个,满足返回true,同时消费n个Token。反之不消费Token,false

    对应线上场景,请求速率过快,直接丢到某些请求

    2.5、Reserve/ReserveN
    func (lim *Limiter) Reserve() *Reservation
    func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation
    

    Reserve相当于ReserveN(time.Now(), 1)。

    ReserveN的用法相对复杂,当调用完后,无论Token是否充足,都会返回一个Reveration*对象

    可以调用该对象的Delay()方法,该方法返回需要等待的时间。如果等待时间为0,无需等待。反之必须等到等待时间后,才能进行接下来的操作。

    如果不想等待可以调用Cancel()方法,该方法会将Token归还。

    ReserveN 返回一个 Reservation,指示调用者在 n 个事件发生之前必须等待多长时间。 限制器在允许未来事件时会考虑此保留。 如果 n 超过限制器的突发大小,则返回的 Reservation 的 OK() 方法返回 false。

    用法示例:

    r := lim.ReserveN(time.Now(), 1)
    if !r.OK() {
        // Not allowed to act! Did you remember to set lim.burst to be > 0 ?
        return
    }
    time.Sleep(r.Delay())
    Act()
    
    2.6、动态速率

    Limiter支持可以调整速率和桶大小:

    SetLimit(Limit) //改变放入Token的速率
    SetBurst(int)//改变Token桶大小
    

    根据当前需求、环境、条件,动态改变Token桶大小和速率

    3、实例代码

    package main
    
    import (
    	"context"
    	"log"
    	"time"
    
    	"golang.org/x/time/rate"
    )
    
    func main() {
    	//TODO limit每秒产生Token数量,burst最多存放Token数量
    	limit := rate.Every(time.Second * 1)
    	limiter := rate.NewLimiter(limit, 5)
    	log.Println(limiter.Limit(), limiter.Burst())
    	for i := 0; i < 10; i++ {
    		//TODO 阻塞等待,直到获取一个Token
    		log.Println("before wait")
    		c, _ := context.WithTimeout(context.Background(), time.Second*2)
    		if err := limiter.Wait(c); err != nil {
    			log.Println("limiter wait err :" + err.Error())
    		}
    		log.Println("after wait")
    		//TODO 返回需要等待多久获取新的Token,等待指定时间执行任务
    		r := limiter.Reserve()
    		log.Println("reserve Delay:", r.Delay())
    		//TODO 判断当前是否可以取到Token
    		a := limiter.Allow()
    		log.Println("Allow:", a)
    
    	}
    }
    
  • 相关阅读:
    快速幂 笔记与思路整理
    倍增法求最近公共祖先 笔记与思路整理
    Dijkstra算法 笔记与思路整理
    appium搭建环境过程 ---新手总结(大牛勿喷,新手互相交流)
    selenium 学习笔记 ---新手学习记录(10) 问题总结(java)--poi--excel 操作
    selenium 学习笔记 ---新手学习记录(9) 问题总结(java)
    selenium 学习笔记 ---新手学习记录(8) 问题总结(java)
    selenium 学习笔记 ---新手学习记录(7) 问题总结(java)
    时间函数
    os 模块
  • 原文地址:https://www.cnblogs.com/remixnameless/p/15083775.html
Copyright © 2011-2022 走看看