redisson-注解限流
实现功能
- 注解实现依赖
- 注解可以重复添加且条件满足即生效
引入 依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.23.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
定义注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(RequestLimiters.class)
@Documented
public @interface RequestLimiter {
String key();
/**
* 默认:单机
* @return
*/
RateType type() default RateType.PER_CLIENT;
/**
* 间隔速率
*
* @return
*/
long rate() default 100;
/**
* 间隔
*
* @return
*/
long interval() default 1000;
RateIntervalUnit timeUnit() default RateIntervalUnit.MILLISECONDS;
}
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestLimiters {
RequestLimiter[] value();
}
定义切面
public class RequestLimiterAspect {
private RedissonClient redissonClient;
public RequestLimiterAspect(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
// @Pointcut("@annotation(requestLimiter)")
// public void pointcut(RequestLimiter requestLimiter) {
//
// }
//
// @Around(value = "pointcut(requestLimiter)", argNames = "pjp,requestLimiter")
// public Object around(ProceedingJoinPoint pjp, RequestLimiter requestLimiter) throws Throwable {
// RRateLimiter limiter = getRateLimiter(requestLimiter);
// if (limiter.tryAcquire(1)) {
// return pjp.proceed();
// } else {
// throw new RateLimitException("too many requests");
// }
// }
@Pointcut("@annotation(com.example.redis.frame.RequestLimiters)||@annotation(com.example.redis.frame.RequestLimiter)")
public void pointcuts() {
}
@Around(value = "pointcuts()", argNames = "pjp")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
RequestLimiter[] annotations = signature.getMethod().getAnnotationsByType(RequestLimiter.class);
//按照时间排序 间隔越长,优先匹配!!
//所以加重复注解时,需要把duration 长的时间放在最上面
for (RequestLimiter requestLimiter : annotations) {
tryRateLimit(requestLimiter);
}
return pjp.proceed();
}
private void tryRateLimit(RequestLimiter requestLimiter) {
RRateLimiter limiter = getRateLimiter(requestLimiter);
System.out.println(requestLimiter.key() + " permit: " + limiter.availablePermits());
if (!limiter.tryAcquire(1)) {
//只要 有一个不满足,就失败
throw new RateLimitException("too many requests: " + requestLimiter.key());
}
}
private RRateLimiter getRateLimiter(RequestLimiter requestLimiter) {
RRateLimiter rateLimiter = redissonClient.getRateLimiter(requestLimiter.key());
rateLimiter.trySetRate(requestLimiter.type(), requestLimiter.rate(), requestLimiter.interval(), requestLimiter.timeUnit());
return rateLimiter;
}
}
配置
@Configuration
public class ReteLimitConfiguration {
@Autowired
private RedissonClient redissonClient;
@Bean
public RequestLimiterAspect aspect(){
return new RequestLimiterAspect(redissonClient);
}
}
public class RateLimitException extends RuntimeException{
public RateLimitException(String message) {
super(message);
}
}
@RestControllerAdvice
public class GlobalExceptionAdvice {
@ResponseBody
@ExceptionHandler(value = RateLimitException.class)
public Map errorHandler(RateLimitException ex) {
Map map = new HashMap();
map.put("code", 100);
map.put("msg", ex.getMessage());
return map;
}
}
测试
@RestController
public class IndexApi {
@Autowired
private StringRedisTemplate redisTemplate;
@RequestLimiter(key = "test",rate = 1,interval = 10,timeUnit = RateIntervalUnit.SECONDS)
@GetMapping("/set")
public String set(@RequestParam String key, @RequestParam String val) {
redisTemplate.opsForValue().set(key, val);
return "success";
}
/**
* duration 越长,放在上面,就像 test3在test4上面
* @param key
* @param val
* @return
*/
@RequestLimiter(key = "test3",rate = 3,interval = 20,timeUnit = RateIntervalUnit.SECONDS)
@RequestLimiter(key = "test4",rate = 2,interval = 10,timeUnit = RateIntervalUnit.SECONDS)
@GetMapping("/set2")
public String set2(@RequestParam String key, @RequestParam String val) {
redisTemplate.opsForValue().set(key, val);
return "success";
}
}
不出意外,set2接口先出现
{
"msg": "too many requests: test4",
"code": 100
}
# 再出现
{
"msg": "too many requests: test3",
"code": 100
}
主要是试试重复注解的使用
good luck!