1. 定义注解声明
/**
* 接口限流注解
*
* @author panzx
**/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface ServerLimit {
/**
* 接口控制标识,不同的key不同的流量控制
*/
String key() default "";
/**
* 每秒最大流量
*/
int limitPerSecond() default 10;
}
2. 定义切面
/**
* 限流切面
*
* @author panzx
**/
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class ServerLimitAspect {
private final HttpServletRequest request;
/**
* key:归档同一组限流方案 => 用户名 + @ServerLimit.key
*/
private final Map<String, RateLimiter> limiterMap = Maps.newConcurrentMap();
@Around("@annotation(com.dlm.openapi.annotation.ServerLimit)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
ServerLimit serverLimit = method.getAnnotation(ServerLimit.class);
if (serverLimit == null) {
return joinPoint.proceed();
}
// 根据实际情况选择是否需要这个
String userName = "";
// 限流key由租户编码 + 接口名称组成
String limitKey = userName + Constant.Character.UNDERLINE + serverLimit.key();
RateLimiter rateLimiter = limiterMap.get(limitKey);
if (rateLimiter == null) {
rateLimiter = RateLimiter.create(serverLimit.limitPerSecond());
// 缓存令牌
limiterMap.put(limitKey, rateLimiter);
}
// 拿令牌
boolean acquire = rateLimiter.tryAcquire(50, TimeUnit.MILLISECONDS);
// 拿不到令牌直接抛出异常
if (!acquire) {
throw new ServerLimitedException();
}
return joinPoint.proceed();
}
}
3. 完事
@GetMapping(value = "test")
@ServerLimit(key = "test", limitPerSecond = 5)
public BaseResponse test() {
return BaseResponse.createSuccess();
}
4. 简简单单的限流方案,进阶的建议使用Redis实现~
本文介绍了如何在Spring框架中使用注解和AOP实现接口限流,通过`ServerLimit`注解配置限流参数,`ServerLimitAspect`切面负责处理限流逻辑,示例了简单的限流方案,并提到进阶应用时可考虑使用Redis作为存储机制。

被折叠的 条评论
为什么被折叠?



