【SpringBoot应用篇】【AOP+注解】SpringBoot+Guava基于注解实现接口限流
- pom
- @RateLimiter
- RateLimiterAspect
- 统一返回实体R
- 抽象Controller基类BaseController
- RateLimiterController
pom
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.2.RELEASEversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
<dependency>
<groupId>com.google.guavagroupId>
<artifactId>guavaartifactId>
<version>31.1-jreversion>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.8.10version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
@RateLimiter
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RateLimiter {
int NOT_LIMITED = 0;
@AliasFor("qps")
double value() default NOT_LIMITED;
@AliasFor("value")
double qps() default NOT_LIMITED;
int timeout() default 0;
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}
RateLimiterAspect
@Slf4j
@Aspect
@Component
public class RateLimiterAspect {
private static final ConcurrentMap<String, com.google.common.util.concurrent.RateLimiter> RATE_LIMITER_CACHE = new ConcurrentHashMap<>();
@Pointcut("@annotation(cn.zysheep.annotation.RateLimiter)")
public void rateLimit() {
}
@Around("rateLimit()")
public Object pointcut(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
RateLimiter rateLimiter = AnnotationUtils.findAnnotation(method, RateLimiter.class);
if (rateLimiter != null && rateLimiter.qps() > RateLimiter.NOT_LIMITED) {
double qps = rateLimiter.qps();
if (RATE_LIMITER_CACHE.get(method.getName()) == null) {
RATE_LIMITER_CACHE.put(method.getName(), com.google.common.util.concurrent.RateLimiter.create(qps));
}
log.debug("【{}】的QPS设置为: {}", method.getName(), RATE_LIMITER_CACHE.get(method.getName()).getRate());
if (RATE_LIMITER_CACHE.get(method.getName()) != null && !RATE_LIMITER_CACHE.get(method.getName()).tryAcquire(rateLimiter.timeout(), rateLimiter.timeUnit())) {
throw new RuntimeException("请求频繁,请稍后再试~");
}
}
return point.proceed();
}
}
统一返回实体R
@Getter
@Setter
@SuppressWarnings({"AlibabaClassNamingShouldBeCamel"})
@Accessors(chain = true)
public class R<T> {
public static final String DEF_ERROR_MESSAGE = "系统繁忙,请稍候再试";
public static final String HYSTRIX_ERROR_MESSAGE = "请求超时,请稍候再试";
public static final int SUCCESS_CODE = 0;
public static final int FAIL_CODE = -1;
public static final int TIMEOUT_CODE = -2;
public static final int VALID_EX_CODE = -9;
public static final int OPERATION_EX_CODE = -10;
private int code;
private T data;
private String msg = "ok";
private String path;
private Map<String, Object> extra;
private long timestamp = System.currentTimeMillis();
private R() {
super();
}
public R(int code, T data, String msg) {
this.code = code;
this.data = data;
this.msg = msg;
}
public static <E> R<E> result(int code, E data, String msg) {
return new R<>(code, data, msg);
}
public static <E> R<E> success(E data) {
return new R<>(SUCCESS_CODE, data, "ok");
}
public static R<Boolean> success() {
return new R<>(SUCCESS_CODE, true, "ok");
}
public static <E> R<E> success(E data, String msg) {
return new R<>(SUCCESS_CODE, data, msg);
}
public static <E> R<E> fail(int code, String msg) {
return new R<>(code, null, (msg == null || msg.isEmpty()) ? DEF_ERROR_MESSAGE : msg);
}
public static <E> R<E> fail(String msg) {
return fail(OPERATION_EX_CODE, msg);
}
public static <E> R<E> fail(String msg, Object... args) {
String message = (msg == null || msg.isEmpty()) ? DEF_ERROR_MESSAGE : msg;
return new R<>(OPERATION_EX_CODE, null, String.format(message, args));
}
public static <E> R<E> fail(BaseExceptionCode exceptionCode) {
return validFail(exceptionCode);
}
public static <E> R<E> fail(BizException exception) {
if (exception == null) {
return fail(DEF_ERROR_MESSAGE);
}
return new R<>(exception.getCode(), null, exception.getMessage());
}
public static <E> R<E> fail(Throwable throwable) {
return fail(FAIL_CODE, throwable != null ? throwable.getMessage() : DEF_ERROR_MESSAGE);
}
public static <E> R<E> validFail(String msg) {
return new R<>(VALID_EX_CODE, null, (msg == null || msg.isEmpty()) ? DEF_ERROR_MESSAGE : msg);
}
public static <E> R<E> validFail(String msg, Object... args) {
String message = (msg == null || msg.isEmpty()) ? DEF_ERROR_MESSAGE : msg;
return new R<>(VALID_EX_CODE, null, String.format(message, args));
}
public static <E> R<E> validFail(BaseExceptionCode exceptionCode) {
return new R<>(exceptionCode.getCode(), null,
(exceptionCode.getMsg() == null || exceptionCode.getMsg().isEmpty()) ? DEF_ERROR_MESSAGE : exceptionCode.getMsg());
}
public static <E> R<E> timeout() {
return fail(TIMEOUT_CODE, HYSTRIX_ERROR_MESSAGE);
}
public R<T> put(String key, Object value) {
if (this.extra == null) {
this.extra = Maps.newHashMap();
}
this.extra.put(key, value);
return this;
}
public Boolean getIsSuccess() {
return this.code == SUCCESS_CODE || this.code == 200;
}
public Boolean getIsError() {
return !getIsSuccess();
}
@Override
public String toString() {
return JSONObject.toJSONString(this);
}
}
抽象Controller基类BaseController
public abstract class BaseController {
public <T> R<T> success(T data) {
return R.success(data);
}
public R<Boolean> success() {
return R.success();
}
public <T> R<T> fail(String msg) {
return R.fail(msg);
}
public <T> R<T> fail(String msg, Object... args) {
return R.fail(msg, args);
}
public <T> R<T> fail(int code, String msg) {
return R.fail(code, msg);
}
public <T> R<T> fail(BaseExceptionCode exceptionCode) {
return R.fail(exceptionCode);
}
public <T> R<T> fail(BizException exception) {
return R.fail(exception);
}
public <T> R<T> fail(Throwable throwable) {
return R.fail(throwable);
}
public <T> R<T> validFail(String msg) {
return R.validFail(msg);
}
public <T> R<T> validFail(String msg, Object... args) {
return R.validFail(msg, args);
}
public <T> R<T> validFail(BaseExceptionCode exceptionCode) {
return R.validFail(exceptionCode);
}
}
RateLimiterController
@Slf4j
@RestController
public class RateLimiterController extends BaseController {
@RateLimiter(qps = 2, timeout = 100)
@GetMapping("/rateLimiter")
public R rateLimiter() {
log.info("【noRateLimiter】被执行了。。。。。");
return success();
}
@GetMapping("/noRateLimiter")
public R noRateLimiter() {
log.info("【noRateLimiter】被执行了。。。。。");
return success();
}
}