用Redis写一个IP限流器

Redis 作为一个高效的内存存储系统,可以用来构建各种类型的过滤器。下面是一个使用 Redis 实现简单请求过滤的例子。在这个例子中,我们将实现一个基于 IP 的限流过滤器,即通过 Redis 来限制某个 IP 在一定时间内的请求次数。

项目配置

首先,你需要在 Spring Boot 项目中添加 Redis 相关的依赖。在 pom.xml 中添加以下内容:

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-web</artifactId>
</dependency>

配置 Redis

application.ymlapplication.properties 文件中配置 Redis 连接信息。

yaml 复制代码
spring:
  redis:
    host: localhost
    port: 6379

实现过滤器

创建一个过滤器类,用于实现基于 IP 的限流功能。

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.Filter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

@Component
public class RateLimitFilter extends OncePerRequestFilter {

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final String RATE_LIMIT_PREFIX = "rate_limit:";
    private static final int MAX_REQUESTS = 5;
    private static final long TIME_WINDOW_SECONDS = 60;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String clientIp = getClientIp(request);
        String key = RATE_LIMIT_PREFIX + clientIp;

        // 获取 Redis 操作对象
        ValueOperations<String, String> ops = redisTemplate.opsForValue();

        // 获取当前请求计数
        String countStr = ops.get(key);
        int count = (countStr == null) ? 0 : Integer.parseInt(countStr);

        if (count >= MAX_REQUESTS) {
            // 超过限制,返回 429 错误
            response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
            response.getWriter().write("Too many requests");
        } else {
            // 增加计数
            if (count == 0) {
                ops.set(key, "1", TIME_WINDOW_SECONDS, TimeUnit.SECONDS);
            } else {
                ops.increment(key);
            }
            filterChain.doFilter(request, response);
        }
    }

    private String getClientIp(HttpServletRequest request) {
        String clientIp = request.getHeader("X-Forwarded-For");
        if (clientIp == null || clientIp.isEmpty() || "unknown".equalsIgnoreCase(clientIp)) {
            clientIp = request.getHeader("Proxy-Client-IP");
        }
        if (clientIp == null || clientIp.isEmpty() || "unknown".equalsIgnoreCase(clientIp)) {
            clientIp = request.getHeader("WL-Proxy-Client-IP");
        }
        if (clientIp == null || clientIp.isEmpty() || "unknown".equalsIgnoreCase(clientIp)) {
            clientIp = request.getRemoteAddr();
        }
        return clientIp;
    }
}

注册过滤器

你需要在 Spring Boot 应用中注册这个过滤器。你可以通过创建一个配置类来实现。

java 复制代码
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<RateLimitFilter> rateLimitFilter() {
        FilterRegistrationBean<RateLimitFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new RateLimitFilter());
        registrationBean.addUrlPatterns("/api/*"); // 你可以根据需求设置 URL 模式
        registrationBean.setOrder(1); // 设置过滤器的顺序
        return registrationBean;
    }
}

小结

通过上述步骤,你实现了一个基于客户端 IP 的限流过滤器。在这个例子中:

  1. RateLimitFilter 类是具体的过滤器实现:

    • 它使用了 Redis 来存储每个 IP 地址的请求计数。
    • 如果在指定时间窗口内请求次数超过限制,那么请求会被拒绝,并返回 HTTP 429 错误。
  2. FilterConfig 类将过滤器注册到 Spring Boot 应用中,使其对指定的 URL 模式生效。

你可以根据业务需求调整请求的限制次数和时间窗口,以及添加更多的自定义处理逻辑。通过这种方式,你可以有效地控制请求流量,避免滥用问题。

相关推荐
夜泉_ly2 小时前
MySQL -安装与初识
数据库·mysql
qq_529835353 小时前
对计算机中缓存的理解和使用Redis作为缓存
数据库·redis·缓存
月光水岸New5 小时前
Ubuntu 中建的mysql数据库使用Navicat for MySQL连接不上
数据库·mysql·ubuntu
狄加山6755 小时前
数据库基础1
数据库
我爱松子鱼5 小时前
mysql之规则优化器RBO
数据库·mysql
chengooooooo6 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
Rverdoser7 小时前
【SQL】多表查询案例
数据库·sql
Galeoto7 小时前
how to export a table in sqlite, and import into another
数据库·sqlite
希忘auto7 小时前
详解Redis在Centos上的安装
redis·centos
人间打气筒(Ada)7 小时前
MySQL主从架构
服务器·数据库·mysql