springboot-aop-redis-lua 实现的分布式限流方案
warning:
这篇文章距离上次修改已过190天,其中的内容可能已经有所变动。
在Spring Boot项目中使用AOP和Redis实现分布式限流,可以通过Lua脚本与Redis配合使用,以确保操作的原子性。以下是一个简化的示例:
- 首先,添加依赖到你的
pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 创建一个Lua脚本来实现限流逻辑:
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local current = tonumber(redis.call('get', key) or "0")
if current + 1 > limit then
return false
else
redis.call('INCR', key)
redis.call('EXPIRE', key, 10)
return true
end
- 在Spring Boot应用中配置AOP和Redis:
@Configuration
public class RedisConfig {
@Bean
public DefaultRedisScript<Boolean> redisRateLimiterScript() {
DefaultRedisScript<Boolean> script = new DefaultRedisScript<>();
script.setScriptText(new ClassPathResource("rate_limiter.lua").getInputStream());
script.setResultType(Boolean.class);
return script;
}
}
- 创建一个注解用于标记需要限流的方法:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimiter {
int limit() default 20;
int timeout() default 10;
}
- 创建AOP切面和切点来应用限流逻辑:
@Aspect
@Component
public class RateLimiterAspect {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private DefaultRedisScript<Boolean> redisRateLimiterScript;
@Around("@annotation(rateLimiter)")
public Object aroundRateLimitedMethods(ProceedingJoinPoint joinPoint, RateLimiter rateLimiter) throws Throwable {
// 生成key
String key = "rate_limit:" + joinPoint.getSignature().toLongString();
// 执行Lua脚本
Boolean allowed = stringRedisTemplate.execute(redisRateLimiterScript, Collections.singletonList(key), Collections.singletonList(String.valueOf(rateLimiter.limit())));
if (Boolean.TRUE.equals(allowed)) {
// 如果允许访问,则继续执行方法
return joinPoint.proceed();
} else {
// 如果不允许访问,抛出异常或返回错误信息
throw new RuntimeException("Too many requests");
}
}
}
- 在需要限流的方法上使用
@RateLimiter
注解:
@RestController
public class TestController {
@RateLimiter(limit = 10, timeout = 60)
@GetMapping("/test")
public String test() {
r
评论已关闭