用Redis写一个IP限流器

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

项目配置

首先,你需要在 Spring Boot 项目中添加 Redis 相关的依赖。在 pom.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 连接信息。

spring:
  redis:
    host: localhost
    port: 6379

实现过滤器

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

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 应用中注册这个过滤器。你可以通过创建一个配置类来实现。

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 模式生效。

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

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot结合Redis实现接口限流的方法有很多种。一种常见的方法是使用Redis的计数和过期时间来实现。在Spring Boot中,可以使用注解和拦截来实现接口限流。 首先,需要在Redis中设置一个key,用于记录接口的访问次数。可以使用Redis的INCR命令来实现计数的功能。然后,可以设置该key的过期时间,以限制在一定时间内的访问次数。 在Spring Boot中,可以使用自定义注解和拦截来实现接口限流。可以在接口上添加一个自定义的注解,用于标识该接口需要进行限流。然后,可以编一个拦截,在拦截中判断接口是否需要进行限流,并根据限流的规则进行处理。 具体实现的代码可以参考[1]中的示例代码。在示例代码中,使用了自定义的注解@Limit来标识需要进行限流的接口。通过设置注解的参数,可以指定限流的规则,如限制访问次数和过期时间。拦截会在接口被访问时进行判断,如果超过了限制的访问次数,则不允许再访问接口。 另外,[2]中的文章介绍了使用Redis和Lua脚本实现接口限流的方法。通过在Redis中设置key的过期时间和访问次数,可以实现接口的限流。该方法可以根据客户端的访问IP或访问接口的频率进行限流。 总之,Spring Boot结合Redis可以实现接口的限流功能,可以根据具体的需求选择合适的方法来实现。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值