java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring Boot  IP 限流

Spring Boot 实现 IP 限流(保障系统稳定性的关键技术)

作者:微特尔普拉斯

在Spring Boot中实现IP限流是一种简单而有效的方式来保障系统的稳定性和可用性,本文通过实例代码讲解Spring Boot实现IP限流的相关操作,感兴趣的朋友一起看看吧

一、引言

在高并发的互联网应用中,系统稳定性面临严峻挑战。恶意攻击、爬虫、以及不合理的接口调用都可能导致系统资源耗尽,影响正常用户体验。为了保障系统的稳定性和可用性,对请求进行限流是至关重要的技术手段。本文将深入探讨如何在 Spring Boot 中实现 IP 限流,包括其原理、使用场景、优缺点、多种实现方式,并给出完整的代码案例,助您构建更加健壮的应用。

二、IP 限流原理:深入解析

IP 限流,顾名思义,就是根据客户端的 IP 地址对请求进行频率限制。其核心在于控制每个 IP 地址在单位时间内允许访问的次数,从而避免单个 IP 大量占用资源,保证系统整体的公平性。以下介绍两种常用的限流算法:

2.1 令牌桶算法 (Token Bucket)

2.2 漏桶算法 (Leaky Bucket)

2.3 令牌桶与漏桶算法对比

特性令牌桶算法漏桶算法
流量控制允许突发流量,平均速率限制严格控制速率,平滑输出
适用场景需要容忍一定突发流量的场景,如 API 限流对速率有严格要求的场景,如消息队列削峰填谷
实现复杂度相对简单相对简单
动态调整容易实现动态调整令牌生成速率和桶容量调整输出速率和桶容量相对困难

三、IP 限流的使用场景:应用广泛

IP 限流作为一种基础且有效的限流手段,在各种场景中都有广泛的应用:

3.1 API 网关限流

在 API 网关层实现 IP 限流,可以统一管理和控制所有 API 的请求频率,是微服务架构中保护后端服务的关键措施。

3.2 Web 应用安全防护

对登录、注册、找回密码等敏感接口进行 IP 限流,防止暴力 破解和恶意注册,增强应用的安全性。

3.3 微服务架构

在微服务架构中,对每个微服务的接口进行 IP 限流,保障服务的稳定性和可用性,防止服务雪崩。

3.4 防止恶意爬虫

限制爬虫程序的访问频率,防止爬虫过度抓取数据,影响正常用户体验。

3.5 控制资源使用

在共享资源的系统中,限制单个 IP 地址的资源使用,确保每个用户都能公平地使用资源。

四、IP 限流的优缺点分析:理性看待

4.1 优点

4.2 缺点

五、Spring Boot 实现 IP 限流:代码案例

以下提供两种基于 Spring Boot 的 IP 限流实现方式:

5.1 基于 Guava RateLimiter 的 IP 限流过滤器(推荐)

这种方式使用 Guava 的 RateLimiter 实现令牌桶算法,简单高效,易于配置。

1. 项目搭建:

创建一个 Spring Boot 项目,并添加以下依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>31.1-jre</version>
    </dependency>
</dependencies>

2. 创建 RateLimiterService:

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.RateLimiter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class RateLimiterService {
    private static final Logger logger = LoggerFactory.getLogger(RateLimiterService.class);
    @Value("${rate.limit.permitsPerSecond:10}") // 默认值 10
    private double permitsPerSecond;
    // 使用 LoadingCache 缓存每个 IP 的 RateLimiter
    private final LoadingCache<String, RateLimiter> rateLimiterCache = CacheBuilder.newBuilder()
            .expireAfterWrite(1, TimeUnit.MINUTES) // 缓存 1 分钟
            .build(new CacheLoader<String, RateLimiter>() {
                @Override
                public RateLimiter load(String ip) {
                    logger.info("Creating RateLimiter for IP: {}, rate: {}", ip, permitsPerSecond);
                    return RateLimiter.create(permitsPerSecond);
                }
            });
    /**
     * 尝试获取令牌
     * @param ip 客户端 IP 地址
     * @return true: 允许访问, false: 限流
     */
    public boolean tryAcquire(String ip) {
        RateLimiter rateLimiter = rateLimiterCache.getUnchecked(ip);
        return rateLimiter.tryAcquire();
    }
}

3. 创建 IP 限流过滤器:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class IpRateLimitFilter extends OncePerRequestFilter {
    private static final Logger logger = LoggerFactory.getLogger(IpRateLimitFilter.class);
    @Autowired
    private RateLimiterService rateLimiterService;
    @Value("${rate.limit.enabled:true}")
    private boolean rateLimitEnabled;
    @Value("${rate.limit.excludePatterns:}") // 排除的URL,用逗号分隔
    private String excludePatterns;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        if (!rateLimitEnabled) {
            filterChain.doFilter(request, response);
            return; // 如果禁用限流,直接放行
        }
        // 排除不需要限流的URL
        String requestURI = request.getRequestURI();
        if (excludePatterns != null && !excludePatterns.isEmpty()) {
             String[] patterns = excludePatterns.split(",");
             for (String pattern : patterns) {
                if (requestURI.startsWith(pattern.trim())) {
                    filterChain.doFilter(request, response);
                    return;
                 }
             }
        }
        String clientIp = getClientIp(request);
        if (rateLimiterService.tryAcquire(clientIp)) {
            filterChain.doFilter(request, response); // 允许访问
        } else {
            logger.warn("Rate limit exceeded for IP: {}", clientIp);
            response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
            response.getWriter().write("Too many requests from this IP.");
        }
    }
    private String getClientIp(HttpServletRequest request) {
        String xffHeader = request.getHeader("X-Forwarded-For");
        if (xffHeader == null) {
            return request.getRemoteAddr();
        }
        return xffHeader.split(",")[0];
    }
}

4. 配置过滤器:

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<IpRateLimitFilter> ipRateLimitFilterRegistration(IpRateLimitFilter ipRateLimitFilter) {
        FilterRegistrationBean<IpRateLimitFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(ipRateLimitFilter);
        registration.addUrlPatterns("/*"); // 对所有请求进行限流
        registration.setOrder(1);
        return registration;
    }
}

5. 创建测试控制器:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
    @GetMapping("/test")
    public String test() {
        return "Hello, this is a test API.";
    }
}

6. application.properties 配置:

# 限流配置
rate.limit.enabled=true
rate.limit.permitsPerSecond=10
rate.limit.excludePatterns=/health,/metrics # 排除的URL

5.2 基于 ConcurrentHashMap 的 IP 限流

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class IPRequestRateLimiter implements HandlerInterceptor {
    private final int REQUEST_LIMIT = 10;  // 每秒最大请求数
    private final Map<String, AtomicInteger> requestCountsPerIP = new ConcurrentHashMap<>();
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String ipAddress = request.getRemoteAddr();
        // 1. 初始化计数器
        requestCountsPerIP.computeIfAbsent(ipAddress, k -> new AtomicInteger(0));
        // 2. 检查请求是否超限
        if (requestCountsPerIP.get(ipAddress).incrementAndGet() > REQUEST_LIMIT) {
            response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
            response.getWriter().write("Too many requests from this IP. Please try again later.");
            return false; // 阻止请求
        }
        return true; // 允许请求
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        String ipAddress = request.getRemoteAddr();
        // 3. 请求完成后,减少计数
        requestCountsPerIP.get(ipAddress).decrementAndGet();
    }
}

注意: 此方式只是一个简易的实现,存在时间窗口不精准等问题,生产环境不建议使用.

5.3 测试

启动 Spring Boot 项目,使用工具(如 Postman)模拟不同 IP 地址的请求。当某个 IP 的请求频率超过配置的限制时,会收到 Too many requests from this IP. 的响应。

六、进阶特性:提升 IP 限流的可靠性

七、总结

在 Spring Boot 中实现 IP 限流是一种简单而有效的方式来保障系统的稳定性和可用性。通过本文的详细介绍和代码案例,相信您已经掌握了 IP 限流的基本原理和实现方式。在实际应用中,请根据您的具体需求选择合适的限流算法和实现方式,并结合其他安全措施,构建更加健壮的系统。请记住,安全策略应该是全面的,限流只是其中一部分。

到此这篇关于Spring Boot 实现 IP 限流(保障系统稳定性的关键技术)的文章就介绍到这了,更多相关Spring Boot  IP 限流内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文