@Aspect
@Component
public class LimitInterceptor {@Autowiredprivate RedissonClient redissonClient;@Value("${stnet.api.limit}")private boolean limit;/***配置织入点***/@Pointcut("@annotation(com.st.microservice.plugin.sso.annotation.Limit)")public void logPointCut() {}@Before(value = "logPointCut()")public void interceptor(JoinPoint pjp) {//限流开关if(limit ==false){return;}MethodSignature signature = (MethodSignature) pjp.getSignature();Method method = signature.getMethod();Limit limit = method.getAnnotation(Limit.class);if(limit.limitType() == LimitType.IP) {String key = "limit:" + IpUtils.getIpAddr();redissonRateLimiter(limit, key);}if(limit.limitType() == LimitType.METHOD) {String key = "limit:" + method.getClass().getSimpleName()+method.getName();redissonRateLimiter(limit, key);}if(limit.limitType() == LimitType.IP_METHOD) {String key = "limit:" + IpUtils.getIpAddr()+method.getClass().getSimpleName()+method.getName();redissonRateLimiter(limit, key);}//开放接口if(limit.limitType() == LimitType.USER_ID) {if(ContextUtils.getUser()!= null) {String key = "limit:" + ContextUtils.getUser().getUserId();redissonRateLimiter(limit, key);}}}/*** 获取Redisson的RRateLimiter** @return*/private void redissonRateLimiter(Limit limit, String key) {RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);if (!rateLimiter.isExists()) {//创建一个RRateLimiter限流器,限流次数,注意count的值不能小于1,必须大于等于1rateLimiter.trySetRate(RateType.OVERALL, limit.count(),limit.period(), RateIntervalUnit.SECONDS);}// 获取限流的配置信息RateLimiterConfig rateLimiterConfig = rateLimiter.getConfig();// 上次配置的限流时间毫秒值Long rateInterval = rateLimiterConfig.getRateInterval();// 上次配置的限流次数Long rate = rateLimiterConfig.getRate();// 将timeOut转换成毫秒之后再跟rateInterval进行比较if (TimeUnit.MILLISECONDS.convert(limit.period(), TimeUnit.SECONDS) != rateInterval || limit.count() != rate) {// 如果rateLimiterConfig的配置跟我们注解上面的值不一致,说明服务器重启过,程序员又修改了限流的配置rateLimiter.delete();// 以程序员重启后的限流配置为准,重新设置rateLimiter.setRate(RateType.OVERALL, limit.count(), limit.period(), RateIntervalUnit.SECONDS);}//超时等待if (limit.actor() == 1) {rateLimiter.acquire();}//快速响应if (limit.actor() == 2) {//获取一个令牌,如果超过5秒没获取就超时处理if(rateLimiter.tryAcquire(1,limit.timeout(),TimeUnit.SECONDS)){return;}throw new ServiceException("请求频繁,请稍后再试!", 400);}}
}
public enum LimitType {/*** 请求者IP -- 通常是某个IP 不过会有人进行虚拟IP进行访问*/IP,/*** 方法级别限流* key = ClassName+MethodName*/METHOD,/*** 特定参数* key = PARAM+MethodName*/USER_ID,/*** 用户级别限流* key = USER_ID*/IP_METHOD/*** 用户级别限流* key = ClassName+MethodName+Ip*/
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Limit {/***给定的时间范围 单位(秒)*/int period() default 5;/***一定时间内最多访问次数*/int count() default 2;/*** 限流的类型 */LimitType limitType() default LimitType.IP_METHOD;/*** 限流的类型 1 超时等待 2 快速返回*/int actor() default 1;/***一超时时间秒,当actor=2是生效*/int timeout() default 3;}