实现 Spring Boot Redis 滑动窗口限流

在开发过程中,我们常常需要对请求进行限流,以防止服务器过载。Redis 滑动窗口是一种常用的限流策略。本文将带你一步步实现 Spring Boot 项目中的 Redis 滑动窗口限流,适合初学者。

流程概述

以下是实现 Redis 滑动窗口的步骤:

步骤 描述 需要的代码
1 创建 Spring Boot 项目 使用 Spring Initializr 工具
2 添加 Redis 依赖 pom.xml 中添加 Redis 依赖
3 配置 Redis application.ymlapplication.properties
4 实现滑动窗口算法 编写 RedisService 类
5 编写拦截器或过滤器实现限流 创建 RateLimitInterceptor 类
6 测试滑动窗口限流 编写测试用例

详细步骤

1. 创建 Spring Boot 项目

首先,你可以使用 [Spring Initializr]( 创建一个新的 Spring Boot 项目,选择 Web 和 Redis 依赖。

2. 添加 Redis 依赖

pom.xml 中添加 Redis 相关依赖:

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

3. 配置 Redis

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

spring:
  redis:
    host: localhost
    port: 6379

4. 实现滑动窗口算法

创建一个 RedisService 类来处理滑动窗口的逻辑。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class RedisService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 限流方法
    public boolean tryAcquire(String key, int limit, long windowSize) {
        long currentTime = System.currentTimeMillis();
        long timeWindowStart = currentTime - windowSize;

        // 添加当前请求时间戳
        redisTemplate.opsForList().rightPush(key, currentTime);
        // 删除超出时间窗口的请求
        redisTemplate.opsForList().remove(key, 0, timeWindowStart);
        
        Long count = redisTemplate.opsForList().size(key);
        
        // 设置过期时间
        redisTemplate.expire(key, windowSize, TimeUnit.MILLISECONDS);
        
        // 判断是否超过限制
        return count <= limit;
    }
}
  • tryAcquire 方法用来尝试获取请求的许可。
  • rightPush 将当前请求的时间戳存入 Redis 列表。
  • remove 方法删除超出时间窗口的请求。
  • size 方法获取当前请求数量,判断是否超出限制。

5. 编写拦截器或过滤器实现限流

创建 RateLimitInterceptor 类:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class RateLimitInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisService redisService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String key = request.getRequestURI();
        int limit = 5; // 每个窗口的请求限制
        long windowSize = 60000; // 1分钟的时间窗口

        if (!redisService.tryAcquire(key, limit, windowSize)) {
            response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
            return false;
        }
        return true;
    }
}
  • preHandle 方法用于拦截 HTTP 请求并进行限流控制。

6. 测试滑动窗口限流

你可以编写 JUnit 测试用例来验证限流功能是否正常。

流程图 & 类图

流程图

journey
    title 滑动窗口限流流程
    section 请求发送
      用户发送请求: 5: user
    section 请求处理
      处理请求: 4: app
      检查请求限制: 4: app
      超出阈值, 返回 429: 5: app

类图

classDiagram
    class RedisService {
        +boolean tryAcquire(String key, int limit, long windowSize)
    }
    class RateLimitInterceptor {
        +boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    }
    class App {
        <<application>>
    }

    App --> RedisService
    App --> RateLimitInterceptor

结尾

通过上述步骤,你已经成功地实现了 Spring Boot 中的 Redis 滑动窗口限流。掌握了这个过程后,你可以根据自己的业务需求进行更复杂的定制。希望你能在后续的开发中灵活应用这一限流机制,提高系统的稳定性!如有疑问,欢迎提问。