guava之令牌桶算法实现接口限流

实现步骤

1.创建注解RateLimit

import java.lang.annotation.*;

/**
 * 自定义注解:限流令牌桶注解,用以创建令牌桶以及设定令牌桶大小
 */
@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    double limitNum() default 20;  //默认每秒放入桶中20
}

2.创建令牌桶切面RateLimitAspect

import com.google.common.util.concurrent.RateLimiter;
import com.shizhongcai.business.common.annotation.RateLimit;
import com.shizhongcai.business.common.domain.enums.ErrorCodesEnum;
import com.shizhongcai.business.common.exception.BaseException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 令牌桶切面,桶中无令牌进行服务降级
 *
 */
@Component
@Scope
@Aspect
public class RateLimitAspect {

    //用来存放不同接口的RateLimiter令牌桶(key为接口名称,value为RateLimiter)
    //ConcurrentHashMap线程安全
    private ConcurrentHashMap<String, RateLimiter> map = new ConcurrentHashMap<>();

    //令牌桶
    private RateLimiter rateLimiter;

    @Pointcut("@annotation(com.shizhongcai.business.common.annotation.RateLimit)")  //切入点是注解@RateLimit
    public void serviceLimit() {
        //该注解实现的方法体(新建了公用方法体,方面后面写增强)
    }


    @Around("serviceLimit()")   //环绕增强,方法体是logPointCut
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        //获取注解信息
        RateLimit annotation = method.getAnnotation(RateLimit.class);
        double limitNum = annotation.limitNum(); //获取注解每秒加入桶中的token

        //获取方法名
        String methodName = signature.getName();

        //根据方法名获取其rateLimiter令牌桶
        if(map.containsKey(methodName)){
            rateLimiter = map.get(methodName);
        }else {
            //如果该令牌桶未被初始化,则先初始化令牌桶
            map.put(methodName, RateLimiter.create(limitNum));
            rateLimiter = map.get(methodName);
        }
        Object result;
        //尝试获取令牌
        if (rateLimiter.tryAcquire()) {
            //令牌获取成功,顺利执行方法
            result = joinPoint.proceed();
        } else {
            //拒绝了请求(服务降级)
            throw new BaseException(ErrorCodesEnum.REQUEST_TOO_FAST);
        }
        return result;
    }

}

3.使用

 /**
     * 测试接口限流
     * limitNum = 1 表示每秒只能放过1个请求
     * @return
     */
    @RateLimit(limitNum = 1)
    @PostMapping(value = "/testRateLimit")
    public BaseRspVo testRateLimit(){
        return new BaseRspVo<>(Arrays.asList("1","2"));
    }
在请求过快的情况下会返回:
{
  "success": false,
  "msg": "您的请求太快啦!请在稍后再试",
  "errorCode": 10005,
  "data": null
}