令牌桶算法(自定义注解)AOP实现服务限流

1.引入限流算法--令牌桶 和AOP依赖

  <!--引入aop依赖     -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

<!--限流令牌桶依赖  guava一个由谷歌管理的工具库     -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>28.0-jre</version>
        </dependency>

2.自定义限流注解:

//该元注解表示定义的注解只能用于方法上
@Target(value = ElementType.METHOD)
//该元注解表示自定义注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在(用于在运行时动态获取注解信息)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface RateLimiterAnnotation {
    //每秒生成令牌的速率
    double speed();

    //获取令牌的超时时间
    long timeout();
}

3.定义AOP切面,使用环绕通知实现限流

/**
 * @author oulei
 * @服务限流切面
 * @Order:控制的切面的执行顺序,越小越选执行,后结束
 * @date 2019/12/4 11:27
 */
@Component
@Aspect
@Slf4j
@Order(10)
public class RateLimiterAspect  {

    /**
     * 存放每个接口的令牌桶
     */
    private ConcurrentHashMap<String, RateLimiter> limiterMap=new ConcurrentHashMap<String, RateLimiter>();


    /**
     * 定义切入点(需要切面拦截的包)
     * 拦截com.demo.distributed包和所有子包里的任意类的任意方法的执行
     */
    @Pointcut("execution(* com.demo.distributed..*.*(..))")
    public void  pointCut() {
    }


    /**
     * 环绕通知内实现限流
     */
    @Around("pointCut()")
    public Object around (ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        log.info("进入环绕通知");
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        // 使用Java反射技术获取方法上是否有@RateLimiterAnnotation注解类
        RateLimiterAnnotation rateLimiterAnnotation=signature.getMethod().getDeclaredAnnotation(RateLimiterAnnotation.class);
        if(rateLimiterAnnotation == null){
            Object proceed=proceedingJoinPoint.proceed();
            return proceed;
        }
        //如果在方法上获取到RateLimiter注解就继续执行

        //获取注解上的令牌生产速率值
        double speed=rateLimiterAnnotation.speed();
        //获取注解上获取令牌的超时时间
        long  timeOut=rateLimiterAnnotation.timeout();
        RateLimiter rateLimiter=getRateLimiter(speed);
        boolean acquireBool = rateLimiter.tryAcquire(timeOut, TimeUnit.MILLISECONDS);
        //指定时间内没有获取到令牌,走服务的降级处理
        if(!acquireBool){
           serviceDegraded();
        }
        Object proceed = proceedingJoinPoint.proceed();
        return proceed;
    }


    /**
     * 获取RateLimiter对象
     */
    public RateLimiter getRateLimiter(Double speed){
        String requestURI = getRequestUrl();
        RateLimiter rateLimiter = limiterMap.get(requestURI);
        if(rateLimiter==null){
            rateLimiter = RateLimiter.create(speed);
            limiterMap.put(requestURI, rateLimiter);
        }
        return rateLimiter;
    }


    /**
     * 服务降级处理
     */
    public void serviceDegraded() throws IOException {
        // 执行服务降级处理
        log.info("请求url为"+getRequestUrl()+"的方法执行服务的降级处理--");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = attributes.getResponse();
        response.reset();
        response.setHeader("Content-type", "text/html;charset=UTF-8");
        OutputStream outputStream=response.getOutputStream();
        try {
            outputStream.write("服务器忙!请稍后重试!".getBytes());
        } catch (Exception e) {
          log.info("降级处理发生异常:"+e.getMessage());
        } finally {
           outputStream.close();
        }
      }


    /**
     * 获取当前请求的url
     */
    public String  getRequestUrl(){
        // 获取当前的请求url
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String requestURI = request.getRequestURI();
        return requestURI;
    }


}
发布了68 篇原创文章 · 获赞 93 · 访问量 8508

猜你喜欢

转载自blog.csdn.net/qq_34707456/article/details/103537110