使用redisson限流器Ratelimiter实现限流,保证服务正常可用
使用的jar包:
implementation 'org.redisson:redisson-spring-boot-starter:3.13.6'
定义自定义注解
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.springframework.web.bind.annotation.Mapping;
import java.lang.annotation.*;
/**
* redisson限流器
* @author liurui
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestRateLimiter {
/**
* 限流的key
* @return
*/
String key();
/**
* 限流模式,默认单机
* @return
*/
RateType type() default RateType.PER_CLIENT;
/**
* 限流速率,默认每秒10
* @return
*/
long rate() default 10;
/**
* 限流速率
* @return
*/
long rateInterval() default 1000;
/**
* 限流速率单位
* @return
*/
RateIntervalUnit timeUnit() default RateIntervalUnit.MILLISECONDS;
}
定义AOP切面
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@Aspect
@Component
public class RequestRateLimitAspect {
@Autowired
private RedissonClient redisson;
/**
* 根据自定义注解获取切点
* @param requestRateLimiter
*/
@Pointcut("@annotation(requestRateLimiter)")
public void accessLimit(RequestRateLimiter requestRateLimiter) {
}
@Around(value = "accessLimit(requestRateLimiter)", argNames = "pjp,requestRateLimiter")
public Object around(ProceedingJoinPoint pjp, RequestRateLimiter requestRateLimiter) throws Throwable {
// 限流拦截器
RRateLimiter rRateLimiter = getRateLimiter(requestRateLimiter);
if (rRateLimiter.tryAcquire(1)) {
return pjp.proceed();
} else {
return buildErrorResult();
}
}
/**
* 获取限流拦截器
* @param requestRateLimiter
* @return
*/
private RRateLimiter getRateLimiter(RequestRateLimiter requestRateLimiter){
RRateLimiter rRateLimiter = redisson.getRateLimiter(StringUtils.isBlank(requestRateLimiter.key()) ? "default:limiter" : requestRateLimiter.key());
// 设置限流
if(rRateLimiter.isExists()) {
RateLimiterConfig rateLimiterConfig = rRateLimiter.getConfig();
// 判断配置是否更新,如果更新,重新加载限流器配置
if (!Objects.equals(requestRateLimiter.rate(), rateLimiterConfig.getRate())
|| !Objects.equals(requestRateLimiter.timeUnit().toMillis(requestRateLimiter.rateInterval()), rateLimiterConfig.getRateInterval())
|| !Objects.equals(requestRateLimiter.type(), rateLimiterConfig.getRateType())) {
rRateLimiter.delete();
rRateLimiter.trySetRate(requestRateLimiter.type(), requestRateLimiter.rate(), requestRateLimiter.rateInterval(), requestRateLimiter.timeUnit());
}
} else {
rRateLimiter.trySetRate(requestRateLimiter.type(), requestRateLimiter.rate(), requestRateLimiter.rateInterval(), requestRateLimiter.timeUnit());
}
return rRateLimiter;
}
/**
* 没有限流令牌返回结果
* @return
*/
public Object buildErrorResult() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", "-1");
map.put("message", "Too many people visit");
return map;
}
}
定义url请求:
import org.redisson.api.RateIntervalUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author liurui
* @date 2021/11/17
*/
@RestController
@RequestMapping("/redis/test")
public class RedisController {
private final Logger logger = LoggerFactory.getLogger(RedisController.class);
@Autowired
private RedisService redisService;
@RequestMapping("/add")
// 为方便测试,每两秒生成一个限流令牌
@RequestRateLimiter(key = "test", rate = 1, rateInterval = 2, timeUnit = RateIntervalUnit.SECONDS)
public Object add(String data) {
logger.info("进入请求");
redisService.add("testkey", data);
return "成功";
}
}
jmeter压测配置:
jmeter压测结果:
在声明的上述自定义注解和AOP切面之后,只需要在需要使用限流的请求方法上声明对应的自定义注解及参数配置即可实现限流。