RateLimiter
Google开源工具包Guava提供了限流工具类RateLimiter,基于令牌桶算法实现。
1.maven依赖:
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>27.1-jre</version></dependency>
2.自定义注解
importjava.lang.annotation.*;importjava.util.concurrent.TimeUnit;/**
* 令牌桶注解实现
*/@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceRequestLimiter{/**
* 每秒创建令牌个数,默认:10
*/doubleQPS()default10D;/**
* 获取令牌等待超时时间 默认:500
*/longtimeout()default500;/**
* 超时时间单位 默认:毫秒
*/TimeUnittimeunit()defaultTimeUnit.MILLISECONDS;/**
* 无法获取令牌返回提示信息
*/Stringmsg()default"请稍后再试!";}
3.拦截器
importcom.alibaba.fastjson.JSON;importcom.alibaba.fastjson.JSONObject;importcom.google.common.util.concurrent.RateLimiter;importcom.tiam.panshi.cloud.appback.annotation.RequestLimiter;importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Component;importorg.springframework.web.method.HandlerMethod;importorg.springframework.web.servlet.HandlerInterceptor;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.PrintWriter;importjava.util.Map;importjava.util.concurrent.ConcurrentHashMap;@Component@Slf4jpublicclassRequestLimitingInterceptorimplementsHandlerInterceptor{privatefinalMap<String,RateLimiter> rateLimiterMap=newConcurrentHashMap<>();@OverridepublicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler){//这里可以抽出去定义返回信息JSONObject jsonObject=newJSONObject();
jsonObject.put("10001","玩命加载中,请稍后再试");try{if(handlerinstanceofHandlerMethod){HandlerMethod handlerMethod=(HandlerMethod) handler;RequestLimiter rateLimit= handlerMethod.getMethodAnnotation(RequestLimiter.class);//判断是否有注解if(rateLimit!=null){// 获取请求urlString url= request.getRequestURI();RateLimiter rateLimiter;// 判断map集合中是否有创建好的令牌桶if(!rateLimiterMap.containsKey(url)){// 创建令牌桶,以n r/s往桶中放入令牌
rateLimiter=RateLimiter.create(rateLimit.QPS());
rateLimiterMap.put(url, rateLimiter);}
rateLimiter= rateLimiterMap.get(url);// 获取令牌boolean acquire= rateLimiter.tryAcquire(rateLimit.timeout(), rateLimit.timeunit());if(acquire){//获取令牌成功returntrue;}else{
log.warn("请求被限流,url:{}", request.getServletPath());makeResult(response,renderJson(jsonObject));returnfalse;}}}returntrue;}catch(Exception var6){
var6.printStackTrace();makeResult(response,renderJson(jsonObject));returnfalse;}}privatevoidmakeResult(HttpServletResponse response,JSONObject jo){
response.setContentType("application/json; charset=utf-8");
response.setCharacterEncoding("UTF-8");try(PrintWriter out= response.getWriter()){
out.append(jo.toJSONString());}catch(Exception e){
e.printStackTrace();}}privateJSONObjectrenderJson(Object o){returnJSONObject.parseObject(JSON.toJSONString(o));}
4.注册拦截器
@ConfigurationpublicclassWebMvcConfigextendsWebMvcConfigurationSupport{/**
* 请求限流拦截器
*/@AutowiredprotectedRequestLimitingInterceptor requestLimitingInterceptor;@OverridepublicvoidaddInterceptors(InterceptorRegistry registry){// 请求限流
registry.addInterceptor(requestLimitingInterceptor).addPathPatterns("/**");}}
5.在接口上配置注解
@RequestLimiter(QPS=5D, timeout=200, timeunit=TimeUnit.MILLISECONDS,msg="玩命加载中,请稍后再试")@GetMapping("/test")@ResponseBodypublicStringtest(){return"";}