Spring Boot接口限流实战:从单机到分布式,一文搞定!
你心头一紧,强撑着睡意打开电脑,发现罪魁祸首竟是一个刚刚上线的推广活动,带来的突发流量像洪水一样冲垮了你的服务……
这样的场景,是否让你感到后怕?事实上,缺乏有效的流量控制,再健壮的系统在突如其来的流量洪峰面前,也如同不设防的城池,一击即溃。
今天,我们就来聊聊如何为你的 Spring Boot 应用构筑一道坚固的防线——接口限流。
方式一:使用 Google Guava 的 RateLimiter (单机令牌桶)
适用场景:单服务实例限流,实现简单,平滑突发流量。
1. 添加依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 创建自定义限流注解
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public@interface RateLimit {
// 每秒允许的请求数
doublevalue();
// 超时时间(默认不等待,立即返回)
longtimeout()default0;
// 超时时间单位
TimeUnit timeUnit()default TimeUnit.MILLISECONDS;
}
3. 实现 AOP 切面
import com.google.common.util.concurrent.RateLimiter;
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.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
publicclassRateLimitAspect {
privatestaticfinal ConcurrentHashMap<String, RateLimiter> RATE_LIMITERS = newConcurrentHashMap<>();
@Around("@annotation(rateLimit)")
public Object doAround(ProceedingJoinPoint joinPoint, RateLimit rateLimit)throws Throwable {
StringmethodName= joinPoint.getSignature().toLongString();
doublepermitsPerSecond= rateLimit.value();
longtimeout= rateLimit.timeout();
TimeUnittimeUnit= rateLimit.timeUnit();
// 获取或创建RateLimiter
RateLimiterrateLimiter= RATE_LIMITERS.computeIfAbsent(
methodName, k -> RateLimiter.create(permitsPerSecond)
);
boolean acquired;
if (timeout > 0) {
acquired = rateLimiter.tryAcquire(timeout, timeUnit);
} else {
acquired = rateLimiter.tryAcquire();
}
if (acquired) {
return joinPoint.proceed();
} else {
thrownewRuntimeException("请求过于频繁,请稍后再试");
}
}
}
4. 在 Controller 中使用
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
publicclassTestController {
// QPS限制为2,最多等待500毫秒
@GetMapping("/test1")
@RateLimit(value = 2.0, timeout = 500, timeUnit = TimeUnit.MILLISECONDS)
public String test1() {
return"test1 success";
}
// QPS限制为1,不等待立即返回
@GetMapping("/test2")
@RateLimit(value = 1.0)
public String test2() {
return"test2 success";
}
}
方式二:使用 Spring Cloud Gateway 或 Netflix Zuul (网关层限流)
适用场景:微服务架构,在网关层统一限流。
1. 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
2. 配置 Redis 和限流规则
spring:
redis:
host:localhost
port:6379
cloud:
gateway:
routes:
-id:user-service
uri:lb://user-service
predicates:
-Path=/api/users/**
filters:
-name:RequestRateLimiter
args:
redis-rate-limiter.replenishRate:10# 每秒允许的请求数
redis-rate-limiter.burstCapacity:20# 令牌桶容量
redis-rate-limiter.requestedTokens:1# 每个请求消耗的令牌数
3. 自定义限流Key(按用户限流)
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
@Configuration
publicclassRateLimitConfig {
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest()
.getHeaders()
.getFirst("Authorization") // 根据token限流
// 或者按IP限流: exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
);
}
}
方式三:使用 Resilience4j (功能丰富的容错库)
适用场景:需要多种 resilience 模式(限流、熔断、重试等)的综合场景。
1. 添加依赖
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 配置限流规则
resilience4j:
ratelimiter:
instances:
userService:
limitForPeriod:10# 时间窗口内允许的请求数
limitRefreshPeriod:1s# 时间窗口长度
timeoutDuration:0# 线程等待时间
allowHealthPermissions:false
3. 在方法上使用注解
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
publicclassUserController {
@GetMapping("/users/{id}")
@RateLimiter(name = "userService", fallbackMethod = "rateLimitFallback")
public String getUser(@PathVariable String id) {
return"User " + id;
}
// 降级方法
private String rateLimitFallback(String id, Exception e) {
return"请求过于频繁,请稍后再试";
}
}
方式四:使用 Redisson + Lua (分布式限流)
适用场景:集群环境下的分布式限流。
1. 添加依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.17.0</version>
</dependency>
2. 创建分布式限流服务
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
@Service
publicclassDistributedRateLimitService {
privatefinal RedissonClient redissonClient;
publicDistributedRateLimitService(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
publicbooleantryAcquire(String key, long rate, long rateInterval) {
RRateLimiterrateLimiter= redissonClient.getRateLimiter(key);
// 设置速率:rate个请求/rateInterval秒
rateLimiter.trySetRate(RateType.OVERALL, rate, rateInterval, RateIntervalUnit.SECONDS);
return rateLimiter.tryAcquire();
}
}
3. 在 Controller 中使用
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
publicclassDistributedController {
privatefinal DistributedRateLimitService rateLimitService;
publicDistributedController(DistributedRateLimitService rateLimitService) {
this.rateLimitService = rateLimitService;
}
@GetMapping("/distributed")
public String distributed() {
Stringkey="api:distributed:" + "user123"; // 按用户ID限流
if (rateLimitService.tryAcquire(key, 10, 60)) { // 60秒内最多10次
return"Success";
} else {
thrownewRuntimeException("请求频率超限");
}
}
}
总结对比
|
方式 |
适用场景 |
优点 |
缺点 |
|---|---|---|---|
| Guava RateLimiter |
单机应用 |
简单易用,性能好 |
不支持分布式 |
| Spring Cloud Gateway |
微服务网关 |
统一入口限流,支持分布式 |
需要网关组件 |
| Resilience4j |
综合容错场景 |
功能丰富,集成多种模式 |
配置相对复杂 |
| Redisson |
分布式集群 |
真正的分布式限流 |
依赖Redis,有网络开销 |
选择建议:
-
如果是单体应用,选择 Guava RateLimiter
-
如果是微服务架构,选择 Spring Cloud Gateway
-
如果需要完整的容错方案,选择 Resilience4j
-
如果需要在业务代码中实现分布式限流,选择 Redisson
根据你的具体业务场景和技术架构,选择最适合的限流方式即可。
更多推荐
所有评论(0)