Java接口限流方案
Lamantin 人气:0RateLimiter
Google开源工具包Guava提供了限流工具类RateLimiter,基于令牌桶算法实现。
1.maven依赖:
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>27.1-jre</version> </dependency>
2.自定义注解
import java.lang.annotation.*; import java.util.concurrent.TimeUnit; /** * 令牌桶注解实现 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequestLimiter { /** * 每秒创建令牌个数,默认:10 */ double QPS() default 10D; /** * 获取令牌等待超时时间 默认:500 */ long timeout() default 500; /** * 超时时间单位 默认:毫秒 */ TimeUnit timeunit() default TimeUnit.MILLISECONDS; /** * 无法获取令牌返回提示信息 */ String msg() default "请稍后再试!"; }
3.拦截器
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.google.common.util.concurrent.RateLimiter; import com.tiam.panshi.cloud.appback.annotation.RequestLimiter; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @Component @Slf4j public class RequestLimitingInterceptor implements HandlerInterceptor { private final Map<String, RateLimiter> rateLimiterMap = new ConcurrentHashMap<>(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { //这里可以抽出去定义返回信息 JSONObject jsonObject = new JSONObject(); jsonObject.put("10001", "玩命加载中,请稍后再试"); try { if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; RequestLimiter rateLimit = handlerMethod.getMethodAnnotation(RequestLimiter.class); //判断是否有注解 if (rateLimit != null) { // 获取请求url String 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) { //获取令牌成功 return true; } else { log.warn("请求被限流,url:{}", request.getServletPath()); makeResult(response, renderJson(jsonObject)); return false; } } } return true; } catch (Exception var6) { var6.printStackTrace(); makeResult(response, renderJson(jsonObject)); return false; } } private void makeResult(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(); } } private JSONObject renderJson(Object o) { return JSONObject.parseObject(JSON.toJSONString(o)); }
4.注册拦截器
@Configuration public class WebMvcConfig extends WebMvcConfigurationSupport { /** * 请求限流拦截器 */ @Autowired protected RequestLimitingInterceptor requestLimitingInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { // 请求限流 registry.addInterceptor(requestLimitingInterceptor).addPathPatterns("/**"); } }
5.在接口上配置注解
@RequestLimiter(QPS = 5D, timeout = 200, timeunit = TimeUnit.MILLISECONDS,msg = "玩命加载中,请稍后再试") @GetMapping("/test") @ResponseBody public String test(){ return ""; }
加载全部内容