问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

GuavaRateLimiter实现API限流,这才是正确的姿势!

发布网友 发布时间:2024-09-26 18:17

我来回答

1个回答

热心网友 时间:2024-11-17 05:19

Guava提供的RateLimiter可以*物理或逻辑资源的被访问速率,咋一听有点像java并发包下的Samephore,但是又不相同,RateLimiter控制的是速率,Samephore控制的是并发量。

RateLimiter的原理类似于令牌桶,它主要由许可发出的速率来定义,如果没有额外的配置,许可证将按每秒许可证规定的固定速度分配,许可将被平滑地分发,若请求超过permitsPerSecond则RateLimiter按照每秒1/permitsPerSecond的速率释放许可。

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>23.0</version></dependency>publicstaticvoidmain(String[]args){Stringstart=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss").format(newDate());RateLimiterlimiter=RateLimiter.create(1.0);//这里的1表示每秒允许处理的量为1个for(inti=1;i<=10;i++){limiter.acquire();//请求RateLimiter,超过permits会被阻塞System.out.println("callexecute.."+i);}Stringend=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss").format(newDate());System.out.println("starttime:"+start);System.out.println("endtime:"+end);}

可以看到,我假定了每秒处理请求的速率为1个,现在我有10个任务要处理,那么RateLimiter就很好的实现了控制速率,总共10个任务,需要9次获取许可,所以最后10个任务的消耗时间为9s左右。那么在实际的项目中是如何使用的呢??

实际项目中使用@ServicepublicclassGuavaRateLimiterService{/*每秒控制5个许可*/RateLimiterrateLimiter=RateLimiter.create(5.0);/***获取令牌**@return*/publicbooleantryAcquire(){returnrateLimiter.tryAcquire();}}@AutowiredprivateGuavaRateLimiterServicerateLimiterService;@ResponseBody@RequestMapping("/ratelimiter")publicResulttestRateLimiter(){if(rateLimiterService.tryAcquire()){returnResultUtil.success1(1001,"成功获取许可");}returnResultUtil.success1(1002,"未获取到许可");}

jmeter起10个线程并发访问接口,测试结果如下:

可以发现,10个并发访问总是只有6个能获取到许可,结论就是能获取到RateLimiter.create(n)中n+1个许可,总体来看Guava的RateLimiter是比较优雅的。本文就是简单的提了下RateLimiter的使用。

翻阅发现使用上述方式使用RateLimiter的方式不够优雅,尽管我们可以把RateLimiter的逻辑包在service里面,controller直接调用即可,但是如果我们换成:自定义注解+切面的方式实现的话,会优雅的多,详细见下面代码:

自定义注解类

importjava.lang.annotation.*;/***自定义注解可以不包含属性,成为一个标识注解*/@Inherited@Documented@Target({ElementType.METHOD,ElementType.FIELD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public@interfaceRateLimitAspect{}

自定义切面类

importcom.google.common.util.concurrent.RateLimiter;importcom.simons.cn.springbootdemo.util.ResultUtil;importnet.sf.json.JSONObject;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Pointcut;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Scope;importorg.springframework.stereotype.Component;importjavax.servlet.ServletOutputStream;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;@Component@Scope@AspectpublicclassRateLimitAop{@AutowiredprivateHttpServletResponseresponse;privateRateLimiterrateLimiter=RateLimiter.create(5.0);//比如说,我这里设置"并发数"为5@Pointcut("@annotation(com.simons.cn.springbootdemo.aspect.RateLimitAspect)")publicvoidserviceLimit(){}@Around("serviceLimit()")publicObjectaround(ProceedingJoinPointjoinPoint){Booleanflag=rateLimiter.tryAcquire();Objectobj=null;try{if(flag){obj=joinPoint.proceed();}else{Stringresult=JSONObject.fromObject(ResultUtil.success1(100,"failure")).toString();output(response,result);}}catch(Throwablee){e.printStackTrace();}System.out.println("flag="+flag+",obj="+obj);returnobj;}publicvoidoutput(HttpServletResponseresponse,Stringmsg)throwsIOException{response.setContentType("application/json;charset=UTF-8");ServletOutputStreamoutputStream=null;try{outputStream=response.getOutputStream();outputStream.write(msg.getBytes("UTF-8"));}catch(IOExceptione){e.printStackTrace();}finally{outputStream.flush();outputStream.close();}}}

推荐一个SpringBoot基础教程及实战示例:https://www.javastack.cn/categories/Spring-Boot/

测试controller类

importcom.simons.cn.springbootdemo.aspect.RateLimitAspect;importcom.simons.cn.springbootdemo.util.ResultUtil;importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.ResponseBody;/***类描述:RateLimit限流测试(基于注解+切面方式)*创建人:simonsfan*/@ControllerpublicclassTestController{@ResponseBody@RateLimitAspect//可以非常方便的通过这个注解来实现限流@RequestMapping("/test")publicStringtest(){returnResultUtil.success1(1001,"success").toString();}

这样通过自定义注解@RateLimiterAspect来动态的加到需要限流的接口上,个人认为是比较优雅的实现吧。

压测结果:

可以看到,10个线程中无论压测多少次,并发数总是*在6,也就实现了限流。

作者:饭一碗

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
手机导航地图语音怎么下载 如何分别真金和仿金首饰 怎样区分真金和仿金首饰呢 小学生新年晚会主持人的串词!!(不要太多)急 大大后天就需要了!!!_百度... 周年晚会策划公司 奥格瑞玛传送门大厅在哪 奥格瑞玛传送门大厅怎么走 锻炼颈椎的几个动作 水多久能结冰 冰能在多长时间内形成 请问水低于0度会结冰吗? 如何防止脱发严重 我刚刚建立了个群,但要怎样把人拉进去? 提高Java 效率的 35 个小技巧,用了的都说好! qq被盗了,能查出来是谁吗? 微信号被盗能查到对方是谁吗? 真菌感染的原因有哪些 真菌感染手足癣怎么治疗 真菌感染皮肤病怎么治疗 按摩涌泉穴危害 ...减速?加速减速有什么用。我觉得向右向左运动就行。磁通量也会... 涌泉穴痛是什么原因 ...闭合导体做切割磁感线运动的速度增大,磁通量 一定会增大吗... 一个线圈在匀强磁场中做加速运动时,线圈中会不会有感应电流?为什么?穿... 北京高校毕业生就业指导中心学历认证 室内机空调滴水怎么办? 怎么坐船去济州岛,济州岛市旅游路线 去济州岛要办签证吗 国内看afreecatv直播教程 请问中央音乐学院的钢琴考级1--9级,初级中级高级是怎么划分的?最新的... 抽油烟机十大品牌排名? ...如果采用单场淘汰赛制决出冠亚军,那么共需要比赛多少场? 长寿面(长寿面的做法) 写杭州海皮岛作文400字 孩‏子自卑和拖延有关系吗‏? 小肉蟹煲怎么做 梦见腿上长黑色的花 法律文秘专业学什么 2024就业前景如何 ...好莱坞大片,要很精彩的票房高亿的!不要1990年之前的,越多越好... 尊重与理解的格言 "问君能有几多愁,恰似一江春水向东流",李煜的 抖音最火的晚安优美句子 "问君能有几多愁,恰似一江春水向东流”描写的是愁什么之情??? "问君能有几多愁,恰似一江春水向东流" 造句 问君能有几多愁,恰似一江春水向东流"出自 "问君能有几多愁,恰似一江春水向东流"的意思 "问君能有几多愁",下一句是什么?答案一定要另类 word怎么下载 word免费版本怎么下载 怎么让一个喜欢你的人讨厌你,恨也没关系 怎么能让一个喜欢你的女人讨厌你拜托各位大神 格力H1什么意思? 比54多1/9的数是( ).