首页
需求:使用自定义注解,实现加注解后,对该接口实现限流功能
思路
- 自定义一个注解,注解中添加两个属性,分别是时间和次数,表示在此时间内,最多允许访问的次数
- 新增一个切面,切点为自定义注解,切面方法使用环绕通知,执行正常方法前判断,是否满足限流条件,如果满足,则直接返回已限流错误信息,如果不限流,则正常执行方法后返回。
- 限流条件设计:
使用一个map,用于存储接口名称和调用数据
在调用数据中,判断接口是否限流,调用数据中包含调用开始时间,调用次数。首次调用时,调用时间设置为当前时间,调用次数设置为1。每次调用时,获取当前时间,与调用开始时间做相减,结果与设置的时间段做对比,如果在设置时间段内,则进行次数比较。如果不在,则重置开始调用时间和调用次数。
设置错误类枚举
public enum ErrorCode {
FAIL("9999", "服务器错误");
private String code;
private String msg;
private ErrorCode(String code, String msg) {
this.code = code;
this.msg = msg;
}
public void setCode(String code) {
this.code = code;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getCode() {
return this.code;
}
public String getMsg() {
return this.getMsg();
}
}
设置统一返回类
@Data
public class R<T> implements Serializable {
private static final long serialVersionUID = 1L;
public static final String OK = "0000";
public static final String OK_MSG = "操作成功";
public static final String ERR = "9999";
public static final String ERR_MSG = "操作失败";
public static final R SUCCESS = new R(OK, OK_MSG);
private String code = ERR;
private String msg = "";
private T data;
public static <T> Mono<R<T>> fail(Mono<T> monoData) {
return createR(ERR, ERR_MSG,monoData);
}
public static <T> Mono<R<T>> fail(String message, Mono<T> monoData) {
return createR(ERR, message, monoData);
}
public static <T> Mono<R<T>> fail(String code, String message, Mono<T> monoData) {
return createR(code, message,monoData);
}
public static <T> Mono<R<T>> success(String message, Mono<T> monoData) {
return createR(OK, message,monoData);
}
public static <T> Mono<R<T>> success(Mono<T> monoData) {
return createR(OK, OK_MSG, monoData);
}
public static <T> Mono<R<T>> withErrorCode(ErrorCode errorCode, Mono<T> monoData){
return createR(errorCode.getCode(), errorCode.getMsg(),monoData);
}
private static <T> Mono<R<T>> createR(String code, String msg, Mono<T> monoData){
return monoData.map(data->{
final R r = new R();
r.setData(data);
r.setCode(code);
r.setMsg(msg);
return r;
});
}
public R() {
}
private R(String code, String message) {
this.code = code;
this.msg = message;
}
private R(String code, String message, T result) {
this.code = code;
this.msg = message;
this.data = result;
}
public boolean isSuccess(){
return OK.equals(code);
}
}
添加注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Limit {
int timeLimit() default 5;
int numLimit() default 60;
}
接口调用数据类
@Slf4j
public class CacheValidate {
private long time;
private int invokeNum;
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public int getInvokeNum() {
return invokeNum;
}
public void setInvokeNum(int invokeNum) {
this.invokeNum = invokeNum;
}
public boolean isValidate(Integer timeLimit,Integer numLimit) {
this.invokeNum = invokeNum + 1;
if (System.currentTimeMillis() / 1000 <= time) {
if (invokeNum <= numLimit) {
return true;
} else {
log.info("已被限流");
}
} else {
this.invokeNum = 1;
this.time = System.currentTimeMillis() / 1000 + timeLimit;
return true;
}
return false;
}
}
限制类
@Slf4j
@Component
public class FlowLimit {
private static Map<String, CacheValidate> cache = new HashMap<String, CacheValidate>();
public boolean validNums(String apiName,Integer timeLimit,Integer numLimit) {
if (apiName == null) {
return false;
}
CacheValidate cacheValidate = null;
synchronized (cache) {
cacheValidate = cache.get(apiName);
if (cacheValidate == null) {
cacheValidate = new CacheValidate();
cacheValidate.setTime(System.currentTimeMillis() / 1000 + timeLimit);
cacheValidate.setInvokeNum(1);
cache.put(apiName, cacheValidate);
return true;
}
return cacheValidate.isValidate(timeLimit,numLimit);
}
}
}
设置切面
@Aspect
@Component
public class MyAspect {
@Autowired
private FlowLimit flowLimit;
@Pointcut("@annotation(com.example.demo.anno.Limit)")
public void point() {
}
@Around("point()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Method method = getMethod(joinPoint);
String methodName = getMethodName(method);
Limit limit = method.getAnnotation(Limit.class);
int numLimit = limit.numLimit();
int timeLimit = limit.timeLimit();
if (flowLimit.validNums(methodName,timeLimit,numLimit)) {
return joinPoint.proceed();
}
return R.fail(Mono.just(123));
}
private String getMethodName(Method method) {
String classPath = method.getDeclaringClass().getName();
String methodName = method.getName();
return classPath + "." + methodName;
}
private Method getMethod(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
return method;
}
}
测试
@Limit(numLimit = 1, timeLimit = 1)
public Mono<R<String>> say() {
int i = new Random().nextInt(10);
return R.success(Mono.just("" + i));
}