当前位置: 首页 > 后端技术 > Java

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

时间:2023-04-01 21:18:36 Java

Guava提供的RateLimiter可以限制物理或逻辑资源的访问速率。乍一看有点像java并发包下的Samephore,其实又不一样。RateLimiter控制速率,Samephore控制并发量。RateLimiter的原理类似于令牌桶。它主要由颁发许可证的速率来定义。如果没有额外配置,会按照licensepersecond指定的固定速率分配license,顺利分配license。如果请求超过permitsPerSecond,则RateLimiter以每秒1/permitsPerSecond的速率释放许可。com.google.guavaguava23.0publicstaticvoidmain(String[]args){Stringstart=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss").format(newDate());RateLimiter限制器=RateLimiter.create(1.0);//这里的1表示每秒允许处理的数量为1for(inti=1;i<=10;i++){limiter.acquire();//请求RateLimiter,超过允许会被阻塞System.out.println("调用执行.."+i);}Stringend=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss").format(newDate());System.out.println("开始时间:"+start);System.out.println("endtime:"+end);}可以看到,我假设每秒处理请求的速率为1,现在我有10个任务要处理,那么RateLimiter就实现了很好的控制rate,一共10个task,需要9次获取license,所以最后10个task的消耗时间大概是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个license。总体来说Guava的RateLimiter更加优雅。本文只是简单的提到了RateLimiter的使用。翻了一下,发现上面使用RateLimiter的方法不够优雅。虽然我们可以把RateLimiter的逻辑包裹在service中,controller可以直接调用,但是如果换成:自定义注解+aspect实现,会优雅很多,具体看下面代码:自定义注解类导入java.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;导入org.aspectj.lang.annotation.Aspect;导入org.aspectj.lang.annotation。切入点;导入org.springframework.beans.factory.annotation.Autowired;导入org.springframework.context.annotation.Scope;导入org.springframework.stereotype.Component;导入javax.servlet.ServletOutputStream;导入javax.servlet.http.HttpServletResponse;导入java.io.IOException;@Component@Scope@AspectpublicclassRateLimitAop{@AutowiredprivateHttpServletResponse响应;私有RateLimiterrateLimiter=RateLimiter.create(5.0);//比方说,我这里设置"并发数"为5@Pointcut("@annotation(com.simons.cn.springbootdemo.aspect.RateLimitAspect)")publicvoidserviceLimit(){}@Around("serviceLimit()")publicObjectaround(ProceedingJoinPointjoinPoint){布尔标志=rateLimiter.tryAcquire();对象obj=null;try{if(flag){obj=joinPoint.proceed();}else{Stringresult=JSONObject.fromObject(ResultUtil.success1(100,"failure")).toString();输出(响应,结果);}}catch(Throwablee){e.printStackTrace();}System.out.println("flag="+flag+",obj="+obj);返回对象;}publicvoidoutput(HttpServletResponseresponse,Stringmsg)throwsIOException{响应e.setContentType("application/json;charset=UTF-8");ServletOutputStreamoutputStream=null;试试{outputStream=response.getOutputStream();outputStream.write(msg.getBytes("UTF-8"));}catch(IOExceptione){e.printStackTrace();}最后{outputStream.flush();outputStream.close();}}}推荐一个SpringBoot基础教程和实例:https://www.javastack.cn/cate...测试控制器类importcom.simons.cn.springbootdemo.aspect.RateLimitAspect;importcom.simons.cn。springbootdemo.util.ResultUtil;导入org.springframework.stereotype.Controller;导入org.springframework.web.bind。annotation.RequestMapping;导入org.springframework.web.bind.annotation.ResponseBody;/***类说明:RateLimit限流测试(基于注解+aspect方法)*创建者:simonsfan*/@ControllerpublicclassTestController{@ResponseBody@RateLimitAspect//通过@RequestMapping注解实现限流非常方便("/测试")公共字符串测试(){返回ResultUtil.success1(1001,"成功").toString();这样就可以在需要限流的接口上动态添加自定义注解@RateLimiterAspect。我个人认为这是一个更优雅的实现。压测结果:可以看到10个线程不管压多少次,并发数始终限制在6,实现了限流。作者:一碗饭\来源:blog.csdn.net/fanrenxiang/*article/details/80949079推荐近期文章:1.1,000+Java面试题及答案(2021最新版)2.不要再填屏了if/else没了,试试策略模式,真香!!3.操!Java中xx≠null的新语法是什么?4、SpringBoot2.5发布,深色模式太炸了!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!