谈一谈go中的限流方法以及time/rate限速器

在高并发系统中有三个常见的利器:缓存、限流、降级

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

限流器,从字面上理解就是用来限制流量,有时候流量突增比如秒杀活动,会将后端服务压垮,甚至直接宕机,使用限流器能限制访问后端的流量,起到一个保护作用,被限制的流量,可以根据具体的业务逻辑去处理,直接返回错误或者返回默认值等等

限流方法

目前主流的限流方法有两个:

漏桶限流:

每次请求的时候计算桶的流量,当超过桶的流量时就降级请求

在这里插入图片描述
可以由上图看到,水龙头的速率就是我们的服务器接收到的请求,当请求速率过大的时候,漏桶会在短时间内迅速装满,然后就会拒绝其他的请求,水就会漫出桶外,对应的请求就会被丢弃,漏桶算法可以强行限制请求的访问以及数据的传输速率。

令牌桶限流:

每次请求的时候从令牌桶里面拿令牌,取不到令牌就降级请求
在这里插入图片描述
令牌通算法以一定恒定的速度往桶里面放入令牌,比如放入1000个令牌,每来一次请求,就从令牌通里面拿一个令牌,当令牌被拿完之后,其他服务就拒绝或者等待服务。令牌桶会不断地生成令牌直到把桶填满。后面再产生的令牌就会从桶中溢出。最后桶中可以保存的最大令牌数永远不会超过桶的大小

time/rate限速器

golang内部有一个非常好用的限流器time/rate位于golang.org/x/time/rate

下面主要讲解一下他的一些函数:

rate.NewLimiter(limit,burst)
limit表示每秒产生的token数,burst表示最多存储的token数,这个函数就可以与上方的令牌桶限流的图形成对照。

//例如: 每秒产生200*cpu个数个令牌,最多存储200*cpu个数个令牌。
limiter = rate.NewLimiter(rate.Limit(200.NumCPU()), 200*runtime.NumCPU())

对应的也有对应的三个使用方法:

  • Allow: 返回是否有 token,没有 token 返回 false,如果有token就会消耗 1 个 token 返回 true
  • Wait: 阻塞等待,直到取到 1 个 token
  • Reserve: 返回 token 信息,此外还会返回需要等待多久才有新的 token

其实这三种方法最终都是调用了ReserveN()方法

Wait()方法的调用过程如下:
Wait()会调用WaitN(),WaitN()也会调用ReserveN()

而Allow()方法最终也是调用了ReserveN().ok

// Allow is shorthand for AllowN(time.Now(), 1).
func (lim *Limiter) Allow() bool {
	return lim.AllowN(time.Now(), 1)
}
func (lim *Limiter) AllowN(now time.Time, n int) bool {
	return lim.reserveN(now, n, 0).ok
}

应用demo:
我们创建一个路由的方法,逻辑就是创建一个限流器,当请求不被allow的时候就会打印出当前的限流器的信息,否则会执行对应的服务器方法

func RateLimiter() func(c *SliceRouterContext) {
	l := rate.NewLimiter(1, 2)
	return func(c *SliceRouterContext) {
		if !l.Allow() {
			c.Rw.Write([]byte(fmt.Sprintf("rate limit:%v,%v", l.Limit(), l.Burst())))
			c.Abort()
			return
		}
		c.Next()
	}
}

以下代码是创建一个反向代理服务器,把请求转发到2003和2004端口,并在转发的过程中添加限流的方法


var addr = "127.0.0.1:2002"

// 熔断方案
func main() {
	coreFunc := func(c *middleware.SliceRouterContext) http.Handler {
		rs1 := "http://127.0.0.1:2003/base"
		url1, err1 := url.Parse(rs1)
		if err1 != nil {
			log.Println(err1)
		}

		rs2 := "http://127.0.0.1:2004/base"
		url2, err2 := url.Parse(rs2)
		if err2 != nil {
			log.Println(err2)
		}

		urls := []*url.URL{url1, url2}
		return proxy.NewMultipleHostsReverseProxy(c, urls)
	}
	log.Println("Starting httpserver at " + addr)

	sliceRouter := middleware.NewSliceRouter()
	sliceRouter.Group("/").Use(middleware.RateLimiter())
	routerHandler := middleware.NewSliceRouterHandler(coreFunc, sliceRouter)
	log.Fatal(http.ListenAndServe(addr, routerHandler))
}

我们把2003、2004端口的服务器以及反向代理服务器运行起来,然后把请求发送给反向代理服务器

我们使用curl模拟请求,可以看到如果我们请求的速度过快就会打印**rate limit:1,2%**的信息

➜  cool-boy-klay curl '127.0.0.1:2002/'
http://127.0.0.1:2004/base/
RemoteAddr=127.0.0.1:53291,X-Forwarded-For=127.0.0.1,X-Real-Ip=
headers =map[Accept:[*/*] Accept-Encoding:[gzip] User-Agent:[curl/7.54.0] X-Forwarded-For:[127.0.0.1]]
➜  cool-boy-klay curl '127.0.0.1:2002/'
http://127.0.0.1:2004/base/
RemoteAddr=127.0.0.1:53291,X-Forwarded-For=127.0.0.1,X-Real-Ip=
headers =map[Accept:[*/*] Accept-Encoding:[gzip] User-Agent:[curl/7.54.0] X-Forwarded-For:[127.0.0.1]]
➜  cool-boy-klay curl '127.0.0.1:2002/'
http://127.0.0.1:2004/base/
RemoteAddr=127.0.0.1:53291,X-Forwarded-For=127.0.0.1,X-Real-Ip=
headers =map[Accept:[*/*] Accept-Encoding:[gzip] User-Agent:[curl/7.54.0] X-Forwarded-For:[127.0.0.1]]
➜  cool-boy-klay curl '127.0.0.1:2002/'
rate limit:1,2%                                                                           ➜  cool-boy-klay curl '127.0.0.1:2002/'
http://127.0.0.1:2004/base/
RemoteAddr=127.0.0.1:53291,X-Forwarded-For=127.0.0.1,X-Real-Ip=
headers =map[Accept:[*/*] Accept-Encoding:[gzip] User-Agent:[curl/7.54.0] X-Forwarded-For:[127.0.0.1]]
➜  cool-boy-klay curl '127.0.0.1:2002/'
rate limit:1,2%                                                                           ➜  cool-boy-klay curl '127.0.0.1:2002/'
http://127.0.0.1:2004/base/
RemoteAddr=127.0.0.1:53291,X-Forwarded-For=127.0.0.1,X-Real-Ip=
headers =map[Accept:[*/*] Accept-Encoding:[gzip] User-Agent:[curl/7.54.0] X-Forwarded-For:[127.0.0.1]]
➜  cool-boy-klay curl '127.0.0.1:2002/'
http://127.0.0.1:2003/base/
RemoteAddr=127.0.0.1:53317,X-Forwarded-For=127.0.0.1,X-Real-Ip=
headers =map[Accept:[*/*] Accept-Encoding:[gzip] User-Agent:[curl/7.54.0] X-Forwarded-For:[127.0.0.1]]

参考链接:
常见限流算法之漏桶算法、令牌桶算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值