当前位置: 首页 > news >正文

Spring AOP + Redisson 实现基于注解的分布式限流方案

1. 背景与需求

在高并发系统中,为了防止接口被频繁调用导致服务器过载,我们通常需要对接口进行限流。常见的限流方案包括:

  • 固定窗口滑动窗口令牌桶等算法

  • 基于本地缓存(如 Guava RateLimiter

  • 基于分布式缓存(如 Redis + Lua 脚本)

本文将演示一种简单且高效的实现方案:
使用 Spring AOP + 自定义注解 + Redisson 分布式限流器,实现用户/IP/API 维度的注解式限流。

核心目标:

  • 通过注解轻松配置接口的限流规则。

  • 支持接口级别用户级别IP级别限流。

  • 使用 Redisson 简化 Redis 分布式锁与限流操作。


2. 技术选型

技术作用
Spring AOP切面编程,拦截带有 @RateLimit 注解的方法
Redisson对 Redis 操作的高级封装,提供分布式限流器 RRateLimiter
Redis存储限流状态,支持集群部署,天然分布式

3. 依赖引入

pom.xml 中添加 Redisson 依赖:

<!-- Redisson -->
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.50.0</version>
</dependency>

4. 配置 Redis

application.yml 中配置 Redis 连接信息:

spring:data:redis:host: localhostport: 6379password:ttl: 3600database: 0

5. Redisson 客户端配置

使用 @Configuration 定义 RedissonClient

@Configuration
public class RedissonConfig {@Value("${spring.data.redis.host}")private String redisHost;@Value("${spring.data.redis.port}")private Integer redisPort;@Value("${spring.data.redis.password}")private String redisPassword;@Value("${spring.data.redis.database}")private Integer redisDatabase;@Beanpublic RedissonClient redissonClient() {Config config = new Config();String address = "redis://" + redisHost + ":" + redisPort;SingleServerConfig singleServerConfig = config.useSingleServer().setAddress(address).setDatabase(redisDatabase).setConnectionMinimumIdleSize(1).setConnectionPoolSize(10).setIdleConnectionTimeout(30000).setConnectTimeout(5000).setTimeout(3000).setRetryAttempts(3).setRetryInterval(1500);// 如果有密码则设置密码if (redisPassword != null && !redisPassword.isEmpty()) {singleServerConfig.setPassword(redisPassword);}return Redisson.create(config);}
}

6. 自定义注解

通过注解声明限流规则:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {// 限流key前缀String key() default "";// 每个时间窗口允许的请求数int rate() default 10;// 时间窗口(秒)int rateInterval() default 1;// 限流类型:API、USER、IPRateLimitType limitType() default RateLimitType.USER;// key过期时间(小时)int expireTime() default 1;// 提示信息String message() default "请求过于频繁,请稍后再试";
}

限流类型枚举:

public enum RateLimitType {API,   // 接口级别USER,  // 用户级别IP     // IP级别
}

7. AOP 切面实现

使用切面在方法调用前执行限流逻辑:

@Aspect
@Component
@Slf4j
public class RateLimitAspect {@Resourceprivate RedissonClient redissonClient;@Resourceprivate UserService userService;@Before("@annotation(rateLimit)")public void doBefore(JoinPoint point, RateLimit rateLimit) {// 生成限流KeyString key = generateRateLimitKey(point, rateLimit);RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);rateLimiter.expire(Duration.ofHours(rateLimit.expireTime()));// 设置限流参数:时间窗口内允许请求数rateLimiter.trySetRate(RateType.OVERALL, rateLimit.rate(), rateLimit.rateInterval(), RateIntervalUnit.SECONDS);// 尝试获取令牌if (!rateLimiter.tryAcquire(1)) {throw new BusinessException(ErrorCode.TOO_MANY_REQUEST, rateLimit.message());}}private String generateRateLimitKey(JoinPoint point, RateLimit rateLimit) {StringBuilder keyBuilder = new StringBuilder("rate_limit:");if (!rateLimit.key().isEmpty()) {keyBuilder.append(rateLimit.key()).append(":");}switch (rateLimit.limitType()) {case API:MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();keyBuilder.append("api:").append(method.getDeclaringClass().getSimpleName()).append(".").append(method.getName());break;case USER:try {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if (attributes != null) {HttpServletRequest request = attributes.getRequest();User loginUser = userService.getLoginUser(request);keyBuilder.append("user:").append(loginUser.getId());} else {keyBuilder.append("ip:").append(getClientIP());}} catch (BusinessException e) {keyBuilder.append("ip:").append(getClientIP());}break;case IP:keyBuilder.append("ip:").append(getClientIP());break;default:throw new BusinessException(ErrorCode.SYSTEM_ERROR, "不支持的限流类型");}return keyBuilder.toString();}private String getClientIP() {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if (attributes == null) return "unknown";HttpServletRequest request = attributes.getRequest();String ip = request.getHeader("X-Forwarded-For");if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("X-Real-IP");}if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}if (ip != null && ip.contains(",")) {ip = ip.split(",")[0].trim();}log.info("客户端IP: {}", ip);return ip != null ? ip : "unknown";}
}

8. 使用示例

在需要限流的接口上添加注解即可:

@RestController
@RequestMapping("/api/test")
public class TestController {// 用户级别限流:每秒最多 5 次@RateLimit(rate = 5, rateInterval = 1, limitType = RateLimitType.USER, message = "操作太频繁啦,请稍后重试!")@GetMapping("/userLimit")public String userLimit() {return "用户级别限流测试成功";}// IP级别限流:每分钟 20 次@RateLimit(rate = 20, rateInterval = 60, limitType = RateLimitType.IP)@GetMapping("/ipLimit")public String ipLimit() {return "IP级别限流测试成功";}// 接口级别限流:每秒 10 次@RateLimit(rate = 10, rateInterval = 1, limitType = RateLimitType.API)@GetMapping("/apiLimit")public String apiLimit() {return "接口级别限流测试成功";}
}

9. 工作流程

  1. 注解声明规则:开发者在 Controller 方法上标记 @RateLimit,指定key前缀、速率、时间窗口、key过期时间和限流类型等(未指定将使用默认值)。

  2. AOP 拦截请求:切面拦截注解方法,在方法执行前通过 Redisson 进行令牌检查。

  3. Redis 记录状态:Redisson 将限流数据存入 Redis,实现分布式共享。

  4. 限流生效:若未获取到令牌,直接抛出自定义异常并返回提示信息。


10. 总结

  • 优点

    • 使用注解即可快速启用限流,低侵入。

    • Redisson 提供分布式保证,适用于集群环境。

    • 支持多种维度(API、用户、IP)灵活配置。

  • 适用场景

    • 接口防刷、防爬虫

    • 高频操作(如短信发送、验证码请求)

    • 防止恶意用户短时间内发起过多请求

这套方案简单易用,可作为分布式系统中的通用限流基础组件。

若想更精细的控制限流规则请参考另几篇文章:

https://blog.csdn.net/xundefined/article/details/144200495

https://blog.csdn.net/xundefined/article/details/144300779


通过 Spring AOP + Redisson 的组合,我们可以在不改动业务逻辑的前提下,快速实现高性能、可扩展的分布式限流,让接口在高并发场景下更安全、更稳定。

http://www.dtcms.com/a/399272.html

相关文章:

  • VMware 性能优化完整指南
  • Vue 3 项目实战教程大事件管理系统 (一):从零开始搭建项目基础
  • 手机Nexus5 安装 Linux(3) - python3
  • vue el-form 自定义校验, 校验用户名调接口查重
  • 大型网站开发团队北京市轨道交通建设管理有限公司网站
  • 【力扣LeetCode】 349_两个数组的交集
  • 学校做好网站建设目的优化优化
  • 【论文阅读】-《Attention Is All You Need》(Transformer)
  • 网站开发工程师任职资格关于建设殡葬网站的报告范文
  • 北京市保障性住房建设投资中心网站6山东省住房和城乡建设部网站首页
  • 【SpringBoot】27 核心功能 - Web开发原理 - Spring MVC中的定制化原理
  • 自动化接口框架搭建分享-pytest
  • 锦州市做网站建设部网站职业资格证查询
  • cpp类与对象
  • 网站建设管理工作沈阳企业免费建站
  • 算法训练.15
  • 【算法】滑动窗口(一)-长度最小的子数组
  • Spring / Spring Boot 常用注解
  • 【SQL中Lag()和LEAD()的用法】
  • 怎么做代理人金沙网站网页界面设计与分析
  • java中设计思想和架构理念
  • 比PostMan更简洁易用,一款国产开源免费的接口管理工具 - PostIn
  • 什么网站管理系统好网络工程师自学难吗
  • JavaEE 初阶第二十四期:网络原理,底层框架的“通关密码”(四)
  • conda安装软件包的通道conda-forge
  • 【数据库】sql基本语句
  • 手机网站应该怎么做建设网站那个公司好
  • linux 常用命令(包含:网络工具、进程管理工具、常用的)
  • Android
  • 产品设计前硬件考虑的事/示波器/EMC/EMI/热敏电阻