当前位置:   article > 正文

基于Redisson实现自定义限流注解

基于Redisson实现自定义限流注解

最近需要给第三方系统提供接口 想着给接口加上限流 减轻服务器压力
基于Redisson实现自定义限流注解

依赖

Springboot 版本:2.5.15

<dependency>
  <groupId>org.redisson</groupId>
  <artifactId>redisson-spring-boot-starter</artifactId>
  <version>3.17.6</version>
</dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

自定义@RateLimit注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {

    /**
     * 限流的Key
     */
    String limitKey() default "";

    /**
     * 时间窗口默认设置为1秒
     */
    int rateInterval() default 1;

    /**
     * 发放的许可证数量
     */
    int value() default 100;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

项目启动初始化所有令牌桶

@Component
public class InitializeRateLimit implements ApplicationContextAware {

    @Resource
    private RedissonClient redissonClient;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    	// 获取所有加了@RestController的类
        Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(RestController.class);
        beanMap.forEach((k, v) -> {
            Class<?> controllerClass = v.getClass();
            Method[] allMethods = controllerClass.getSuperclass().getDeclaredMethods();
            RateLimit redisRateLimit;
            RRateLimiter rRateLimiter;
            for (Method method : allMethods) {
            	// 获取加了@RateLimit注解的方法
                if (method.isAnnotationPresent(RateLimit.class)) {
                    redisRateLimit = method.getAnnotation(RateLimit.class);
                    String key = redisRateLimit.limitKey();
                    String simpleName = controllerClass.getSimpleName();
                    key = AssertUtil.isEmpty(key) ? simpleName.substring(0, simpleName.indexOf("$")) + "." + method.getName()
                            : simpleName.substring(0, simpleName.indexOf("$")) + "." + method.getName() + "." + key;
                    // 声明一个限流器        
                    rRateLimiter = redissonClient.getRateLimiter(GeneralConstants.LIMIT_KEY + key);
                    // 设置时间窗口和速率
                    rRateLimiter.trySetRate(RateType.OVERALL, redisRateLimit.value(), redisRateLimit.rateInterval(), RateIntervalUnit.SECONDS);
                    // 塞到Map中
                    RateLimitAspect.rateLimitMap.put(key, rRateLimiter);
                }
            }
        });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

注解切面实现

@Component
@Aspect
@Slf4j
public class RateLimitAspect {

    public static Map<String, RRateLimiter> rateLimitMap = new ConcurrentHashMap<>();

    @Pointcut("@annotation(com.haxchip.common.processor.annotation.RateLimit)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        try {
            Class<?> clazz = joinPoint.getTarget().getClass();
            Signature signature = joinPoint.getSignature();
            String name = signature.getName();
            String limitKey = getLimitKey(clazz, name);
            // 从Map中获取限流器
            RRateLimiter rateLimiter = rateLimitMap.get(limitKey);
            if (!rateLimiter.tryAcquire()) {
            	log.warn("访问过于频繁,限制请求Key: {}", limitKey);
             	throw ExceptionFactory.businessException(SystemErrors.SYSTEM_BUSY);
            }
            return joinPoint.proceed();
        } catch (BusinessException businessException) {
            throw businessException;
        } catch (Throwable e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    private String getLimitKey(Class<?> clazz, String methodName) {
        for (Method method : clazz.getDeclaredMethods()) {
            if (method.getName().equals(methodName)) {
                if (method.isAnnotationPresent(RateLimit.class)) {
                    String key = method.getAnnotation(RateLimit.class).limitKey();
                    return AssertUtil.isEmpty(key) ? clazz.getSimpleName() + "." + method.getName()
                            : clazz.getSimpleName() + "." + method.getName() + "." + key;
                }
            }
        }
        return null;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/934019
推荐阅读
相关标签
  

闽ICP备14008679号