本文将介绍几种在 Spring Boot 项目中实现请求速率限制的方案,包括使用 Spring Cloud Gateway、Nginx、Bucket4j、Spring AOP 和 Spring Interceptor。
使用 Spring Cloud Gateway 实现请求速率限制
1. 创建一个 Spring Boot 项目
首先,我们需要创建一个 Spring Boot 项目。确保 Spring Boot 版本为 2.7.x 或更高。
添加以下依赖到 pom.xml
文件中:
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2. 配置 Spring Cloud Gateway
在项目中,我们需要配置 Spring Cloud Gateway 来启用速率限制功能。打开 application.yml
文件,添加以下配置:
yaml
spring:
cloud:
gateway:
routes:
- id: rate_limit_route
uri: http://httpbin.org:80 # 目标服务的 URL
predicates:
- Path=/get # 匹配路径为 /get 的请求
filters:
- name: RequestRateLimiter # 使用请求速率限制过滤器
args:
redis-rate-limiter.replenishRate: 10 # 每秒钟允许的请求数
redis-rate-limiter.burstCapacity: 20 # 最大突发请求数
redis:
host: localhost # Redis 服务器主机名
port: 6379 # Redis 服务器端口
server:
port: 8080 # 本地服务器端口
3. 添加 Redis 依赖
Spring Cloud Gateway 的速率限制功能依赖于 Redis 作为后端存储。确保你的项目中包含了 Redis 依赖。在 pom.xml
中添加以下依赖:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
4. 运行 Redis
如果你还没有运行 Redis 实例,可以使用 Docker 快速启动一个 Redis 实例:
arduino
docker run -d -p 6379:6379 redis
5. 启动 Spring Boot 应用
确保 Redis 服务已经启动,然后运行 Spring Boot 应用。应用启动后,Spring Cloud Gateway 将根据我们在 application.yml
中的配置,对路径为 /get
的请求进行速率限制。
6. 测试速率限制
可以使用 Postman 或 curl 工具来测试速率限制功能。例如,使用 curl 命令发送请求:
bash
curl http://localhost:8080/get
在短时间内连续发送多次请求,超过配置的速率限制时,你会看到 429 Too Many Requests 的响应。
7. 自定义速率限制键生成器(可选)
如果需要基于更复杂的条件进行速率限制,可以自定义速率限制键生成器。创建一个实现 KeyResolver
接口的类,并注册为 Spring Bean。
自定义 Key Resolver 示例
创建一个自定义的 KeyResolver
类,用于基于 IP 地址进行速率限制:
kotlin
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Configuration
public class RateLimiterConfig {
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
}
在 application.yml
中引用自定义的 KeyResolver
:
yaml
spring:
cloud:
gateway:
routes:
- id: rate_limit_route
uri: http://httpbin.org:80
predicates:
- Path=/get
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 2
key-resolver: "#{@userKeyResolver}"
8. 实现自定义限流逻辑(可选)
你也可以实现自定义限流逻辑,使用 RedisRateLimiter
的扩展性来满足更复杂的需求。例如,创建一个自定义的 RedisRateLimiter
:
typescript
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import reactor.core.publisher.Mono;
@Configuration
public class CustomRateLimiterConfig {
@Bean
public RedisRateLimiter customRedisRateLimiter(ReactiveStringRedisTemplate redisTemplate) {
return new RedisRateLimiter(10, 20) {
@Override
public Mono<Response> isAllowed(String routeId, String id) {
// 自定义限流逻辑
return super.isAllowed(routeId, id);
}
};
}
}
在 application.yml
中引用自定义的 RedisRateLimiter
:
yaml
spring:
cloud:
gateway:
routes:
- id: rate_limit_route
uri: http://httpbin.org:80
predicates:
- Path=/get
filters:
- name: RequestRateLimiter
args:
rate-limiter: "#{@customRedisRateLimiter}"
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
redis-rate-limiter.requestedTokens: 2
使用 Nginx 实现请求速率限制
Nginx 是一个高性能的反向代理服务器和 Web 服务器,它具有内置的速率限制功能。
配置示例
在 Nginx 配置文件中,使用 limit_req_zone
和 limit_req
指令:
ini
http {
# 定义一个限速区域,基于客户端 IP 地址进行限速,每秒最多 10 个请求
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
server {
location /api/ {
# 应用限速规则,允许最大 20 个突发请求
limit_req zone=one burst=20;
proxy_pass http://backend;
}
}
}
解释
limit_req_zone
:定义限流区域,这里基于客户端 IP 地址进行限流,10r/s
表示每秒 10 个请求。limit_req
:应用速率限制,burst=20
表示允许的最大突发请求数。
使用 Bucket4j 实现请求速率限制
Bucket4j 是一个基于 Java 的速率限制库,可以与 Spring Boot 集成使用。
配置示例
添加依赖:
xml
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
<version>6.2.0</version>
</dependency>
创建过滤器
java
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Refill;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Duration;
@Component
public class RateLimitFilter extends OncePerRequestFilter {
// 创建一个限速桶,允许每分钟 10 个请求
private final Bucket bucket;
public RateLimitFilter() {
Bandwidth limit = Bandwidth.classic(10, Refill.greedy(10, Duration.ofMinutes(1)));
this.bucket = Bucket.builder()
.addLimit(limit)
.build();
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// 如果请求可以从桶中获取到令牌,则继续处理请求
if (bucket.tryConsume(1)) {
filterChain.doFilter(request, response);
} else {
// 否则返回 429 Too Many Requests 状态码
response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
}
}
}
注册过滤器
typescript
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class RateLimiterApplication {
public static void main(String[] args) {
SpringApplication.run(RateLimiterApplication.class, args);
}
@Bean
public FilterRegistrationBean<RateLimitFilter> rateLimitFilter() {
FilterRegistrationBean<RateLimitFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new RateLimitFilter());
registrationBean.addUrlPatterns("/api/*");
return registrationBean;
}
}
使用 Spring AOP 实现请求速率限制
通过 Spring AOP,我们可以在方法级别实现速率限制。
添加依赖
在 pom.xml
中添加 Spring AOP 依赖:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
定义注解和 AOP 切面
定义注解
java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 自定义注解,用于标记需要限流的方法
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RateLimited {
int limit() default 5; // 每时间段允许的请求数
int timePeriod() default 60; // 时间段长度,单位为秒
}
定义切面
java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class RateLimiterAspect {
// 存储限流器的映射,键为方法签名,值为限流器实例
private Map<String, SimpleRateLimiter> limiters = new ConcurrentHashMap<>();
// 环绕通知,应用于标记了 @RateLimited 注解的方法
@Around("@annotation(rateLimited)")
public Object rateLimit(ProceedingJoinPoint joinPoint, RateLimited rateLimited) throws Throwable {
String key = joinPoint.getSignature().toShortString();
SimpleRateLimiter limiter = limiters.computeIfAbsent(key, k -> SimpleRateLimiter.create(rateLimited.limit(), rateLimited.timePeriod(), TimeUnit.SECONDS));
if (limiter.tryAcquire()) {
return joinPoint.proceed(); // 允许请求,继续执行方法
} else {
throw new RateLimitExceededException("Too many requests"); // 拒绝请求,抛出异常
}
}
}
定义简单的限流器
java
import com.google.common.util.concurrent.RateLimiter;
import java.util.concurrent.TimeUnit;
public class SimpleRateLimiter {
private final RateLimiter rateLimiter;
// 私有构造函数,创建限流器实例
private SimpleRateLimiter(double permitsPerSecond) {
this.rateLimiter = RateLimiter.create(permitsPerSecond);
}
// 工厂方法,创建限流器实例
public static SimpleRateLimiter create(int limit, int timePeriod, TimeUnit unit) {
double permitsPerSecond = (double) limit / unit.toSeconds(timePeriod);
return new SimpleRateLimiter(permitsPerSecond);
}
// 尝试获取一个令牌,成功则返回 true,否则返回 false
public boolean tryAcquire() {
return rateLimiter.tryAcquire();
}
}
在控制器中使用注解
kotlin
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ApiController {
@RateLimited(limit = 5, timePeriod = 60) // 应用速率限制注解
@GetMapping("/api/data")
public String getData() {
return "This is rate limited data."; // 被限流的方法
}
}
使用 Spring Interceptor 实现请求速率限制
通过 Spring 的拦截器也可以实现简单的速率限制。
配置示例
定义拦截器
java
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@Component
public class RateLimiterInterceptor implements HandlerInterceptor {
// 存储限流器的映射,键为客户端 IP 地址,值为限流器实例
private ConcurrentHashMap<String, SimpleRateLimiter> limiters = new ConcurrentHashMap<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String clientIp = request.getRemoteAddr();
SimpleRateLimiter limiter = limiters.computeIfAbsent(clientIp, k -> SimpleRateLimiter.create(5, 60, TimeUnit.SECONDS));
if (limiter.tryAcquire()) {
return true; // 允许请求
} else {
response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS); // 拒绝请求,返回 429 状态码
return false;
}
}
}
注册拦截器
kotlin
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private RateLimiterInterceptor rateLimiterInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(rateLimiterInterceptor).addPathPatterns("/api/**"); // 为指定路径添加拦截器
}
}
总结
本文介绍了多种在 Spring Boot 项目中实现请求速率限制的方法。根据具体的需求和项目环境,可以选择适合的方案来实现请求速率限制,确保后端服务的稳定性。