SpringBoot如何使用AOP+Redis实现接口限流实现全过程(值得珍藏)

发布时间:2024年01月21日

1. 引言

在当今的微服务架构中,接口限流是一个常见的需求,用以防止系统过载和潜在的资源耗尽。Spring Boot 提供了一种方便的方式来实施接口限流,结合 AOP(面向切面编程)和 Redis 存储限流信息,可以有效地实现这一目标。

2. 实现原理

  1. AOP: AOP 允许程序员定义横切关注点,将通用逻辑从业务逻辑中分离出来。在接口限流中,我们可以使用 AOP 来拦截对特定接口的访问,并检查是否超过了设定的限流阈值。
  2. Redis: Redis 是一个高性能的键值对存储系统,非常适合用作限流信息的存储。我们可以将用户的访问信息存储在 Redis 中,并使用相应的策略来判断是否允许用户访问。

在这里插入图片描述

3. 实现步骤

3.1 添加依赖

首先,你需要在你的 pom.xml 文件中添加 Spring Boot Starter AOP 和 Spring Boot Starter Data Redis 的依赖:

<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-aop</artifactId>  
</dependency>  
<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-data-redis</artifactId>  
</dependency>

3.2 配置 Redis

application.propertiesapplication.yml 文件中配置 Redis 的连接信息:

# application.properties  
spring.redis.host=localhost  
spring.redis.port=6379

3.3 创建 Redis 存储类

在这里插入图片描述

创建一个类来封装与 Redis 交互的逻辑:

@Service  
public class RateLimiterService {  
    @Autowired  
    private StringRedisTemplate redisTemplate;  
    private static final String PREFIX = "rate_limiter:";  
    private static final int DEFAULT_LIMIT = 10; // 默认限流次数  
    private static final int DEFAULT_EXPIRE_TIME = 60; // 默认过期时间(秒)  
  
    public boolean isAllow(@NonNull String key) {  
        // 获取当前时间戳(毫秒)  
        long now = System.currentTimeMillis();  
        // 从 Redis 中获取限流信息(如果存在)  
        String value = redisTemplate.opsForValue().get(PREFIX + key);  
        if (value == null) { // 如果不存在,则设置默认限流信息并返回 true(允许访问)  
            redisTemplate.opsForValue().set(PREFIX + key, String.valueOf(DEFAULT_LIMIT), DEFAULT_EXPIRE_TIME, TimeUnit.SECONDS);  
            return true;  
        } else { // 如果存在,则解析限流信息并返回是否允许访问(根据实际需求修改)  
            int limit = Integer.parseInt(value); // 假设我们使用简单的计数器实现限流,这里只是简单地将计数器减一并判断是否小于零。实际应用中可能需要更复杂的策略。  
            if (limit > 0) { // 如果还有剩余访问次数,则更新 Redis 中的限流信息并返回 true(允许访问)  
                redisTemplate.opsForValue().set(PREFIX + key, String.valueOf(limit - 1), DEFAULT_EXPIRE_TIME, TimeUnit.SECONDS);  
                return true;  
            } else { // 如果已经达到限流次数,则返回 false(拒绝访问)  
                return false;  
            }  
        }  
    }  
}

3.4 创建 AOP 切面

在这里插入图片描述

创建一个 AOP 切面来拦截特定接口的访问:

@Aspect  
@Component  
public class RateLimitAspect {  
  
    @Autowired  
    private RateLimiterService rateLimiterService; // 注入 Redis 存储类,用于处理限流逻辑。  
  
    @Pointcut("execution(* com.example.demo.controller.*.*(..))") // 定义切入点表达式,拦截所有 com.example.demo.controller 包下的方法。你可以根据需要修改这个表达式来拦截特定的接口。  
    public void controllerMethods() {} // 定义一个空的方法,作为切入点表达式的方法签名。这个方法不需要实现任何逻辑。  
  
    @Around("controllerMethods()") // 使用 @Around 注解来定义环绕通知。这个通知会在目标方法执行前后执行。你可以在这里添加自定义的逻辑来处理限流。例如:记录日志、抛出异常等。这里我们只是简单地调用 rateLimiterService 的 isAllow 方法。  
    public Object rateLimit(ProceedingJoinPoint joinPoint) throws Throwable {  
        String key = // 生成唯一的 key,用于标识用户或请求。例如,可以使用用户的 ID 或 IP 地址。  
        if (rateLimiterService.isAllow(key)) {  
            return joinPoint.proceed(); // 如果允许访问,则继续执行目标方法。  
        } else {  
            // 如果达到限流阈值,可以抛出异常或返回特定的响应。  
            throw new CustomRateLimitException("Rate limit exceeded");  
        }  
    }  
}

3.5 处理限流逻辑

在环绕通知中,你可以根据 rateLimiterService.isAllow(key) 的返回值来决定是否允许用户访问:

@Around("controllerMethods()")  
public Object rateLimit(ProceedingJoinPoint joinPoint) throws Throwable {  
    String key = // 生成唯一的 key,用于标识用户或请求。例如,可以使用用户的 ID 或 IP 地址。  
    if (rateLimiterService.isAllow(key)) {  
        return joinPoint.proceed(); // 如果允许访问,则继续执行目标方法。  
    } else {  
        // 如果达到限流阈值,可以抛出异常或返回特定的响应。  
        throw new CustomRateLimitException("Rate limit exceeded");  
    }  
}

3.6 自定义异常

创建一个自定义的异常类来表示限流阈值已达:

public class CustomRateLimitException extends RuntimeException {  
    public CustomRateLimitException(String message) {  
        super(message);  
    }  
}

3.7 配置切面

最后,你需要在 Spring Boot 的配置类中启用 AOP 并配置切面:

@EnableAspectJAutoProxy  
@Configuration  
public class AppConfig {  
    @Bean  
    public RateLimitAspect rateLimitAspect() {  
        return new RateLimitAspect();  
    }  
}

4. 总结

通过结合 AOP 和 Redis,我们可以实现一个灵活且可扩展的接口限流机制。AOP 使得我们可以将限流逻辑与业务逻辑分离,使得代码更加清晰和易于维护。而 Redis 作为一个高性能的存储系统,可以快速地处理大量的限流请求。在实际应用中,你可能需要根据具体需求调整限流策略和 Redis 的使用方式。

文章来源:https://blog.csdn.net/a342874650/article/details/135723756
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。