🧑🎓 个人主页:花棉袄
📖 本章内容:【自定义注解-Redis限流配置】
🌳Gitee仓库地址:👉🏽SpringBoot-Redis限流配置
🕰️Redis配置
- 🌳参考文档:👉🏽SpringBoot-Redis配置
🥗限流脚本
/**
* @Author: 花棉袄
* @Date: 2022年08月17日
* @Describe: redis脚本常量
*/
public class RedisCacheConstant {
@ApiModelProperty("加锁脚本")
public static final String LOCK_LUA = "if redis.call('setNx',KEYS[1],ARGV[1]) == 1 then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end";
@ApiModelProperty("解锁脚本")
public static final String RELEASE_LOCK_LUA = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
@ApiModelProperty("限流脚本")
public static final String LIMITER_LUA = "local key = KEYS[1] local count = tonumber(ARGV[1]) local time = tonumber(ARGV[2]) local current = redis.call('get', key); if current and tonumber(current) > count then return tonumber(current); end current = redis.call('incr', key) if tonumber(current) == 1 then redis.call('expire', key, time) end return tonumber(current)";
}
/**
* @Author: 花棉袄
* @CreateDate: 2022年08月28日
* @Describe: 脚本配置
*/
@Component
public class RedisScript {
/**
* 限流脚本
*
* @return
*/
@Bean
public DefaultRedisScript<Long> limitScript() {
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(RedisCacheConstant.LIMITER_LUA);
redisScript.setResultType(Long.class);
return redisScript;
}
/**
* 加锁脚本
*
* @return
*/
@Bean
public DefaultRedisScript<Long> lockScript() {
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(RedisCacheConstant.LOCK_LUA);
redisScript.setResultType(Long.class);
return redisScript;
}
/**
* 解锁脚本
*/
@Bean
public DefaultRedisScript<Long> ReleaseLockScript() {
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(RedisCacheConstant.RELEASE_LOCK_LUA);
redisScript.setResultType(Long.class);
return redisScript;
}
}
🥙限流注解
/**
* @Author: 花棉袄
* @CreateDate: 2022年08月23日
* @Describe: 限流注解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter {
@ApiModelProperty("限流key")
String key() default RedisCacheConstant.RATE_LIMIT_KEY;
@ApiModelProperty("限流时间,单位秒")
int time() default 60;
@ApiModelProperty("限流次数")
int count() default 100;
@ApiModelProperty("限流类型")
LimitType limitType() default LimitType.DEFAULT;
}
/**
* @Author: 花棉袄
* @CreateDate: 2022年08月23日
* @Describe: 限流类型
*/
public enum LimitType {
@ApiModelProperty("默认策略全局限流")
DEFAULT,
@ApiModelProperty("根据请求者IP进行限流")
IP
}
🍣限流切面类
/**
* @Author: 花棉袄
* @CreateDate: 2022年08月23日
* @Describe: 限流处理处理 切面类
*/
@Aspect
@Component
public class RateLimiterAspect {
private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
private RedisTemplate<Object, Object> redisTemplate;
private RedisScript<Long> limitScript;
@Autowired
public void setRedisTemplate(RedisTemplate<Object, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Autowired
public void setLimitScript(RedisScript<Long> limitScript) {
this.limitScript = limitScript;
}
@Before("@annotation(rateLimiter)")
public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable {
String key = rateLimiter.key();
int time = rateLimiter.time();
int count = rateLimiter.count();
String combineKey = getCombineKey(rateLimiter, point);
List<Object> keys = Collections.singletonList(combineKey);
try {
/* 判断redis中的缓存key的值是否存在且大于限制的次数 */
Long number = redisTemplate.execute(limitScript, keys, count, time);
if (StringUtils.isNull(number) || number.intValue() > count) {
/* 缓存过于频繁 */
throw new ServiceException("Redis限流业务","访问过于频繁,请稍候再试");
}
log.info("限制请求最大次数'{}',当前请求次数'{}',缓存的key'{}'", count, number.intValue(), key);
} catch (ServiceException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("服务器限流异常,请稍候再试");
}
}
public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) {
StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
if (rateLimiter.limitType() == LimitType.IP) {
stringBuffer.append(IpUtils.getIpAddress(ServletUtils.getRequest())).append("-");
}
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
Class<?> targetClass = method.getDeclaringClass();
stringBuffer.append(targetClass.getName()).append("-").append(method.getName());
return stringBuffer.toString();
}
}
🍖限流测试
/**
* @Author: 花棉袄
* @CreateDate: 2022年08月28日
* @Describe: 限流测试
*/
@RestController
@RequestMapping("/test")
public class LimitController {
@RateLimiter
@GetMapping("/limit")
@ApiOperation(value = "", notes = "")
public AjaxResult list() {
return AjaxResult.success();
}
}
📢💨如果文章对你有帮助【关注👍点赞❤️收藏⭐】