You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot应用如何以键值对形式在缓存中存储数据实现用户限流

基于用户ID的Spring Boot限流实现方案

核心思路

利用分布式缓存(如Redis)存储用户请求计数,以用户ID为键,值为该用户的请求次数,同时为每个键设置1分钟的过期时间。每次处理用户请求时:

  1. 检查缓存中是否存在该用户ID的计数记录
  2. 无记录则初始化计数为1,并设置1分钟过期
  3. 有记录则将计数加1,若超过5次则返回限流错误,否则更新缓存计数

具体实现步骤

1. 引入缓存依赖

如果使用Redis作为缓存,在pom.xml中添加依赖:

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

2. 自定义限流注解与AOP切面

通过注解+AOP的方式实现无侵入式限流:

自定义限流注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserRateLimit {
    int maxCount() default 5; // 每分钟最大请求次数
    long expireSeconds() default 60; // 过期时间(秒)
}

AOP切面逻辑实现

@Component
@Aspect
public class UserRateLimitAspect {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Around("@annotation(rateLimit)")
    public Object handleRateLimit(ProceedingJoinPoint joinPoint, UserRateLimit rateLimit) throws Throwable {
        // 从请求上下文获取当前用户ID(根据实际业务调整,比如从Token、Session中解析)
        String userId = getCurrentUserId();
        if (userId == null) {
            throw new IllegalArgumentException("用户身份验证失败");
        }

        String cacheKey = "rate_limit:user:" + userId;
        ValueOperations<String, String> valueOps = redisTemplate.opsForValue();

        String countStr = valueOps.get(cacheKey);
        if (countStr == null) {
            // 首次请求,初始化计数与过期时间
            valueOps.set(cacheKey, "1", rateLimit.expireSeconds(), TimeUnit.SECONDS);
        } else {
            int currentCount = Integer.parseInt(countStr);
            if (currentCount >= rateLimit.maxCount()) {
                throw new RuntimeException("1分钟内搜索次数已达上限,请稍后再试");
            }
            // 计数加1
            valueOps.increment(cacheKey);
        }

        return joinPoint.proceed();
    }

    // 示例:从请求头获取用户ID,实际项目需根据登录机制调整
    private String getCurrentUserId() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        return request.getHeader("X-User-Id");
    }
}

3. 在业务接口上应用注解

在商品搜索接口方法上添加限流注解:

@RestController
@RequestMapping("/goods")
public class GoodsSearchController {

    @UserRateLimit(maxCount = 5, expireSeconds = 60)
    @GetMapping("/search")
    public ResponseEntity<Map<String, Object>> search(@RequestParam String keyword) {
        // 商品搜索业务逻辑
        Map<String, Object> result = new HashMap<>();
        result.put("code", 200);
        result.put("data", "搜索结果:" + keyword);
        return ResponseEntity.ok(result);
    }
}

关键注意事项

  • 分布式场景适配:多实例部署时必须使用Redis等分布式缓存,不能用本地缓存(如Caffeine),否则各实例计数不统一。
  • 异常统一处理:可以自定义限流异常类,通过全局异常处理器返回标准化的JSON错误响应,避免直接抛出RuntimeException。
  • 用户ID可靠性:确保用户ID的获取逻辑稳定,比如登录用户从JWT Token、Spring Security上下文解析,避免出现空值或错误标识。

内容的提问来源于stack exchange,提问作者Nihar Ranjan Khatua

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 04:02:23