后端某些接口在高并发的压力下往往会导致性能的严重下降,为了维持我们后端服务型的高性能和高可用,我们往往可以对某些接口或某些用户去设计限流机制,控制这些热点接口的访问量,我这里利用Redis的高性能优势,并整合AOP编程和引入Lua限流脚本在SpringBoot中对任意接口或某些用户实现了访问量限流的机制,其中,我这里给出了三种限流机制:用户,IP地址,全局限流
1.定义限流方式
/**
* 限流类型
* @Author GuihaoLv
*/
public enum LimitType {
/**
* 默认策略全局限流
*/
DEFAULT,
/**
* 根据请求者IP进行限流
*/
IP,
/**
* 根据请求者的用户ID进行限流
*/
USER,
/**
* 根据请求者的部门进行限流
*/
DEPT,
}
2.引入AOP,自定义限流注解和限流处理的切面类
/**
* 限流注解
* @Author GuihaoLv
*/
//实例 @RateLimiter(time = 60, count = 5, limitType = LimitType.IP) 效果:同一IP 60秒内最多允许5次登录尝试。
@Target(ElementType.METHOD) //表示该注解仅能标注在方法上,用于对具体方法进行限流控制。
@Retention(RetentionPolicy.RUNTIME) //注解在运行时保留,可通过反射机制读取注解信息,实现动态限流逻辑。
@Documented //注解信息会包含在生成的 JavaDoc 中
public @interface RateLimiter {
/**
* 限流key
*/
public String key() default RedisConstant.RATE_LIMIT_KEY;
/**
* 限流时间,单位秒
*/
public int time() default 60;
/**
* 限流次数
*/
public int count() default 100;
/**
* 限流类型
*/
public LimitType limitType() default LimitType.DEFAULT;
}
/**
* 限流处理切面
* @Author GuihaoLv
*/
@Aspect
@Component
//确保仅当配置项 spring.cache.type=redis 时,切面才会生效
@ConditionalOnProperty(prefix = "spring.cache", name = { "type" }, havingValue = "redis", matchIfMissing = false)
public class RateLimiterAspect
{
private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
private RedisTemplate
3. 在Redis的配置类中整合Lua语言自定义限流脚本的执行
/**
* 限流脚本定义
* @return
*/
@Bean
public DefaultRedisScript limitScript() {
DefaultRedisScript redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(limitScriptText()); //加载Lua脚本
redisScript.setResultType(Long.class); // 返回类型为Long
return redisScript;
}
/**
* 限流脚本
*/
private String limitScriptText() {
return "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);";
}
4. 限流注解的使用
案例1:登录接口IP限流
@RateLimiter( key = "login_attempt", time = 300, // 5分钟 count = 5,
limitType = LimitType.IP )
@PostMapping("/login")
public Response login(@RequestBody LoginDTO dto) { // 登录逻辑 }
案例2:API用户维度限流
@RateLimiter( key = "api_v1:data_export", time = 3600, // 1小时 count = 10,
limitType = LimitType.USER )
@GetMapping("/export")
public void exportData() { // 数据导出逻辑 }
这样就能对上述接口实现对应的限流机制了