限流是什么意思 手机流量限流怎么恢复
admin
2023-10-28 01:30:15

time/rate类库是基于令牌桶算法实现的限流功能。前面说令牌桶算法的原理是系统会以一个恒定的速率往桶里放入令牌,那么桶就有一个固定的大小,往桶中放入令牌的速率也是恒定的,并且允许突发流量。查看文档发现一个函数:

限流是什么意思 手机流量限流怎么恢复

funcNewLimiter(rLimit,bint)*Limiter

newLimiter返回一个新的限制器,它允许事件的速率达到r,并允许最多突发b个令牌。也就是说Limter限制时间的发生频率,但这个桶一开始容量就为b,并且装满b个令牌(令牌池中最多有b个令牌,所以一次最多只能允许b个事件发生,一个事件花费掉一个令牌),然后每一个单位时间间隔(默认1s)往桶里放入r个令牌。

limter:=rate.NewLimiter(10,5)

上面的例子表示,令牌桶的容量为5,并且每一秒中就往桶里放入10个令牌。细心的读者都会发现函数NewLimiter第一个参数是Limit类型,可以看源码就会发现Limit实际上就是float64的别名。

//Limitdefinesthemaximumfrequencyofsomeevents.//Limitisrepresentedasnumberofeventspersecond.//AzeroLimitallowsnoevents.typeLimitfloat64

限流器还可以指定往桶里放入令牌的时间间隔,实现方式如下:

limter:=rate.NewLimiter(rate.Every(100*time.Millisecond),5)

这两个例子的效果是一样的,使用第一种方式不会出现在每一秒间隔一下子放入10个令牌,也是均匀分散在100ms的间隔放入令牌。rate.Limiter提供了三类方法用来限速:

Allow/AllowNWait/WaitNReserve/ReserveN

下面对比这三类限流方式的使用方式和适用场景。先看第一类方法:

func(lim*Limiter)Allow()boolfunc(lim*Limiter)AllowN(nowtime.Time,nint)bool

Allow 是AllowN(time.Now(), 1)的简化方法。那么重点就在方法 AllowN上了,API的解释有点抽象,说得云里雾里的,可以看看下面的API文档解释:

AllowNreportswhetherneventsmayhappenattimenow.Usethismethodifyouintendtodrop/skipeventsthatexceedtheratelimit.OtherwiseuseReserveorWait.

实际上就是为了说,方法 AllowN在指定的时间时是否可以从令牌桶中取出N个令牌。也就意味着可以限定N个事件是否可以在指定的时间同时发生。这个两个方法是无阻塞,也就是说一旦不满足,就会跳过,不会等待令牌数量足够才执行。

也就是文档中的第二行解释,如果打算丢失或跳过超出速率限制的时间,那么久请使用该方法。比如使用之前实例化好的限流器,在某一个时刻,服务器同时收到超过了8个请求,如果令牌桶内令牌小于8个,那么这8个请求就会被丢弃。一个小示例:

funcAllowDemo(){limter:=rate.NewLimiter(rate.Every(200*time.Millisecond),5)i:=0for{i++iflimter.Allow(){fmt.Println(i,"====Allow======",time.Now())}else{fmt.Println(i,"====Disallow======",time.Now())}time.Sleep(80*time.Millisecond)ifi==15{return}}}

执行结果:

1====Allow======2019-12-1415:54:09.9852178+0800CSTm=+0.0059980012====Allow======2019-12-1415:54:10.1012231+0800CSTm=+0.1220033013====Allow======2019-12-1415:54:10.1823056+0800CSTm=+0.2030858014====Allow======2019-12-1415:54:10.263238+0800CSTm=+0.2840182015====Allow======2019-12-1415:54:10.344224+0800CSTm=+0.3650042016====Allow======2019-12-1415:54:10.4242458+0800CSTm=+0.4450260017====Allow======2019-12-1415:54:10.5043101+0800CSTm=+0.5250903018====Allow======2019-12-1415:54:10.5852232+0800CSTm=+0.6060034019====Disallow======2019-12-1415:54:10.6662181+0800CSTm=+0.68699830110====Disallow======2019-12-1415:54:10.7462189+0800CSTm=+0.76699910111====Allow======2019-12-1415:54:10.8272182+0800CSTm=+0.84799840112====Disallow======2019-12-1415:54:10.9072192+0800CSTm=+0.92799940113====Allow======2019-12-1415:54:10.9872224+0800CSTm=+1.00800260114====Disallow======2019-12-1415:54:11.0672253+0800CSTm=+1.08800550115====Disallow======2019-12-1415:54:11.1472946+0800CSTm=+1.168074801

第二类方法:因为ReserveN比较复杂,第二类先说WaitN。

func(lim*Limiter)Wait(ctxcontext.Context)(errerror)func(lim*Limiter)WaitN(ctxcontext.Context,nint)(errerror)

类似Wait 是WaitN(ctx, 1)的简化方法。与AllowN不同的是WaitN会阻塞,如果令牌桶内的令牌数不足N个,WaitN会阻塞一段时间,阻塞时间的时长可以用第一个参数ctx进行设置,把 context 实例为context.WithDeadline或context.WithTimeout进行制定阻塞的时长。

funcWaitNDemo(){limter:=rate.NewLimiter(10,5)i:=0for{i++ctx,canle:=context.WithTimeout(context.Background(),400*time.Millisecond)ifi==6{//取消执行canle()}err:=limter.WaitN(ctx,4)iferr!=nil{fmt.Println(err)continue}fmt.Println(i,",执行:",time.Now())ifi==10{return}}}

执行结果:

1 ,执行:2019-12-14 15:45:15.538539 +0800 CST m=+0.0110234012 ,执行:2019-12-14 15:45:15.8395195 +0800 CST m=+0.3120039013 ,执行:2019-12-14 15:45:16.2396051 +0800 CST m=+0.7120895014 ,执行:2019-12-14 15:45:16.6395169 +0800 CST m=+1.1120013015 ,执行:2019-12-14 15:45:17.0385893 +0800 CST m=+1.511073701contextcanceled7 ,执行:2019-12-14 15:45:17.440514 +0800 CST m=+1.9129984018 ,执行:2019-12-14 15:45:17.8405152 +0800 CST m=+2.3129996019 ,执行:2019-12-14 15:45:18.2405402 +0800 CST m=+2.71302460110 ,执行:2019-12-14 15:45:18.6405179 +0800 CST m=+3.113002301

适用于允许阻塞等待的场景,比如消费消息队列的消息,可以限定最大的消费速率,过大了就会被限流避免消费者负载过高。

第三类方法:

func(lim*Limiter)Reserve()*Reservationfunc(lim*Limiter)ReserveN(nowtime.Time,nint)*Reservation

与之前的两类方法不同的是Reserve/ReserveN返回了Reservation实例。Reservation在API文档中有5个方法:

func(r*Reservation)Cancel()//相当于CancelAt(time.Now())func(r*Reservation)CancelAt(nowtime.Time)func(r*Reservation)Delay()time.Duration//相当于DelayFrom(time.Now())func(r*Reservation)DelayFrom(nowtime.Time)time.Durationfunc(r*Reservation)OK()bool

通过这5个方法可以让开发者根据业务场景进行操作,相比前两类的自动化,这样的操作显得复杂多了。通过一个小示例来学习Reserve/ReserveN:

funcReserveNDemo(){limter:=rate.NewLimiter(10,5)i:=0for{i++reserve:=limter.ReserveN(time.Now(),4)//如果为flase说明拿不到指定数量的令牌,比如需要的令牌数大于令牌桶容量的场景if!reserve.OK(){return}ts:=reserve.Delay()time.Sleep(ts)fmt.Println("执行:",time.Now(),ts)ifi==10{return}}}

执行结果:

执行:2019-12-14 16:22:26.6446468 +0800 CST m=+0.008000201 0s执行:2019-12-14 16:22:26.9466454 +0800 CST m=+0.309998801 247.999299ms执行:2019-12-14 16:22:27.3446473 +0800 CST m=+0.708000701 398.001399ms执行:2019-12-14 16:22:27.7456488 +0800 CST m=+1.109002201 399.999499ms执行:2019-12-14 16:22:28.1456465 +0800 CST m=+1.508999901 398.997999ms执行:2019-12-14 16:22:28.5456457 +0800 CST m=+1.908999101 399.0003ms执行:2019-12-14 16:22:28.9446482 +0800 CST m=+2.308001601 399.001099ms执行:2019-12-14 16:22:29.3446524 +0800 CST m=+2.708005801 399.998599ms执行:2019-12-14 16:22:29.7446514 +0800 CST m=+3.108004801 399.9944ms执行:2019-12-14 16:22:30.1446475 +0800 CST m=+3.508000901 399.9954ms

如果在执行Delay()之前操作Cancel()那么返回的时间间隔就会为0,意味着可以立即执行操作,不进行限流。

funcReserveNDemo2(){limter:=rate.NewLimiter(5,5)i:=0for{i++reserve:=limter.ReserveN(time.Now(),4)//如果为flase说明拿不到指定数量的令牌,比如需要的令牌数大于令牌桶容量的场景if!reserve.OK(){return}ifi==6||i==5{reserve.Cancel()}ts:=reserve.Delay()time.Sleep(ts)fmt.Println(i,"执行:",time.Now(),ts)ifi==10{return}}}

执行结果:

1 执行:2019-12-14 16:25:45.7974857 +0800 CST m=+0.007005901 0s2 执行:2019-12-14 16:25:46.3985135 +0800 CST m=+0.608033701 552.0048ms3 执行:2019-12-14 16:25:47.1984796 +0800 CST m=+1.407999801 798.9722ms4 执行:2019-12-14 16:25:47.9975269 +0800 CST m=+2.207047101 799.0061ms5 执行:2019-12-14 16:25:48.7994803 +0800 CST m=+3.009000501 799.9588ms6 执行:2019-12-14 16:25:48.7994803 +0800 CST m=+3.009000501 0s7 执行:2019-12-14 16:25:48.7994803 +0800 CST m=+3.009000501 0s8 执行:2019-12-14 16:25:49.5984782 +0800 CST m=+3.807998401 798.0054ms9 执行:2019-12-14 16:25:50.3984779 +0800 CST m=+4.607998101 799.0075ms10执行:2019-12-14 16:25:51.1995131 +0800 CST m=+5.409033301 799.0078ms

看到这里time/rate的限流方式已经完成,除了上述的三类限流方式,time/rate还提供了动态调整限流器参数的功能。相关API如下:

func(lim*Limiter)SetBurst(newBurstint)//相当于SetBurstAt(time.Now(),newBurst).func(lim*Limiter)SetBurstAt(nowtime.Time,newBurstint)//重设令牌桶的容量func(lim*Limiter)SetLimit(newLimitLimit)//相当于SetLimitAt(time.Now(),newLimit)func(lim*Limiter)SetLimitAt(nowtime.Time,newLimitLimit)//重设放入令牌的速率

这四个方法可以让程序根据自身的状态动态的调整令牌桶速率和令牌桶容量。

结尾

通过上述一系列讲解,相信大家对各个限流的应用场景和优缺点也有了大致的掌握,希望在日常开发中有所帮助。限流仅仅是整个服务治理中的一个小环节,需要与多种技术结合使用,才可以更好的提升服务的稳定性的同时提高用户体验。

原文:https://mp.weixin.qq.com/s/HTQoAo1hVNBn7sNOveReCA

相关内容

热门资讯

苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
怎么样的桃花 桃花近看远看的样... 今年的“三八”妇女节,阳光明媚,踏青赏花正当时。遂与姐妹们共赴十里蓝山,与花海来一场春天的约会。十里...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
怎么样的桃花 桃花近看远看的样... 今年的“三八”妇女节,阳光明媚,踏青赏花正当时。遂与姐妹们共赴十里蓝山,与花海来一场春天的约会。十里...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...