赞
踩
最近需要给第三方系统提供接口 想着给接口加上限流 减轻服务器压力
基于Redisson实现自定义限流注解
依赖
Springboot 版本:2.5.15
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.17.6</version>
</dependency>
自定义@RateLimit注解
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RateLimit { /** * 限流的Key */ String limitKey() default ""; /** * 时间窗口默认设置为1秒 */ int rateInterval() default 1; /** * 发放的许可证数量 */ int value() default 100; }
项目启动初始化所有令牌桶
@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); } } }); } }
注解切面实现
@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; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。