在上一节,我们学习了限流的方案:
- 限流算法(如:令牌桶、漏桶、计数器)
- 应用层解决
- 接入层解决(如Nginx)
我们知道RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流,并且根据系统的实际情况来调整生成token的速率。本文通过仿写RateLimiter来实现限流,下面直接实现代码(讲解主要的代码):
1.Controller层:
@RequestMapping("/myOrder") @ExtRateLimiter(value = 10.0, timeOut = 500) public String myOrder() throws InterruptedException { System.out.println("myOrder"); return "SUCCESS"; }
2.自定义注解:
@Target(value = ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface ExtRateLimiter { double value(); long timeOut(); }
3.AOP切面类:由于每个方法都基本会用到,所以写成切面类,避免代码的重复编写
@Aspect @Component public class RateLimiterAop { // 存放接口是否已经存在 private static ConcurrentHashMap<String, RateLimiter> rateLimiterMap = new ConcurrentHashMap<String, RateLimiter>(); @Pointcut("execution(public * com.xxx.api.*.*(..))") public void rlAop() { } @Around("rlAop()") public Object doBefore(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature(); // 使用Java反射技术获取方法上是否有@ExtRateLimiter注解类 ExtRateLimiter extRateLimiter = signature.getMethod().getDeclaredAnnotation(ExtRateLimiter.class); if (extRateLimiter == null) { // 正常执行方法 Object proceed = proceedingJoinPoint.proceed(); return proceed; } // ############获取注解上的参数 配置固定速率 ############### // 获取配置的速率 double value = extRateLimiter.value(); // 获取等待令牌等待时间 long timeOut = extRateLimiter.timeOut(); RateLimiter rateLimiter = getRateLimiter(value, timeOut); // 判断令牌桶获取token 是否超时 boolean tryAcquire = rateLimiter.tryAcquire(timeOut, TimeUnit.MILLISECONDS); if (!tryAcquire) { serviceDowng(); return null; } // 获取到令牌,直接执行.. Object proceed = proceedingJoinPoint.proceed(); return proceed; } // 获取RateLimiter对象 private RateLimiter getRateLimiter(double value, long timeOut) { // 获取当前URL ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); String requestURI = request.getRequestURI(); RateLimiter rateLimiter = null; if (!rateLimiterMap.containsKey(requestURI)) { // 开启令牌通限流 rateLimiter = RateLimiter.create(value); // 独立线程 rateLimiterMap.put(requestURI, rateLimiter); } else { rateLimiter = rateLimiterMap.get(requestURI); } return rateLimiter; } // 服务降级 private void serviceDowng() throws IOException { // 执行服务降级处理 System.out.println("执行降级方法,服务器忙!请稍后重试!"); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletResponse response = attributes.getResponse(); response.setHeader("Content-type", "text/html;charset=UTF-8"); PrintWriter writer = response.getWriter(); try { writer.println("执行降级方法,服务器忙!请稍后重试!"); } catch (Exception e) { } finally { writer.close(); } } }