Guava RateLimiter:高效流量控制实践指南

背景

在互联网飞速发展的今天,随着系统用户规模的不断扩大和分布式架构的广泛应用,API 接口的稳定性和性能成为系统设计中至关重要的因素。无论是应对突发的流量高峰,还是防止恶意爬虫的恶意请求,限流策略都已成为现代系统不可或缺的一部分,其主要目的包括但不限于以下几点:

保护后端服务:通过限制单位时间内对特定接口的访问次数,可以有效避免因突发流量或恶意攻击导致的服务过载,从而确保后端服务的稳定运行。保证用户体验:合理的限流策略可以在不影响正常用户使用的情况下,控制资源的合理分配,确保大多数用户的请求能够得到及时响应,提升整体服务质量。资源优化利用:对于有限的计算资源,如数据库连接、缓存资源等,通过限流可以避免这些资源被少数高频率请求耗尽,确保资源的有效利用。成本控制:云服务通常按照资源消耗计费,不当的请求可能会导致不必要的成本增加。限流可以帮助企业更好地控制成本,避免因为意外的高流量而导致的成本激增。防止恶意行为:限流可以作为一道防线,阻止恶意爬虫、DDoS攻击等非法行为,保护系统免受损害。数据安全:通过限制对外部数据的访问频率,可以减少敏感信息泄露的风险,特别是在处理个人隐私数据时尤为重要。合规性:某些行业有特定的数据访问规则和限制,实施限流有助于满足这些合规要求,避免法律风险。

Guava 提供了多种实现限流的方法,其中最常用的是 RateLimiter 类。RateLimiter 可以帮助我们控制应用程序的资源消耗速度,例如限制每秒的请求数量。

实现

1.依赖引入:

复制
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.1-jre</version> </dependency>1.2.3.4.5.

2.application.yml 中配置限流参数:

复制
rate-limiter: permits-per-second: 5 # 每秒许可数 warmup-period: 0 # 预热时间(秒) timeout: 0 # 获取许可的超时时间(秒)1.2.3.4.

3.限流配置属性类

复制
@Data @Component @ConfigurationProperties(prefix = "rate-limiter") public class RateLimiterProperties { /** * 每秒许可数 */ private double permitsPerSecond; /** * 预热时间(秒) */ private long warmupPeriod; /** * 获取许可的超时时间(秒) */ private long timeout; }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.

4.配置 RateLimiter

复制
@Configuration public class RateLimiterConfig { /** * 配置 RateLimiter Bean * * @param properties 注入的限流配置属性 * @return RateLimiter 实例 */ @Bean public RateLimiter rateLimiter(RateLimiterProperties properties) { if (properties.getWarmupPeriod() > 0) { // 创建带有预热期的 RateLimiter return RateLimiter.create( properties.getPermitsPerSecond(), properties.getWarmupPeriod(), TimeUnit.SECONDS ); } else { // 创建标准的 RateLimiter return RateLimiter.create(properties.getPermitsPerSecond()); } } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.

5.创建控制器

复制
@RestController public class RateLimiterController { @Autowired private RateLimiter rateLimiter; @Autowired private RateLimiterProperties properties; /** * 测试限流接口 * * @return 请求结果 */ @GetMapping("/api/test") //@RateLimitAspect(qps = 2, timeout = 200, timeUnit = TimeUnit.MILLISECONDS) public ResponseEntity<String> rateApi() { boolean acquired = rateLimiter.tryAcquire(properties.getTimeout(), TimeUnit.SECONDS); if (acquired) { // 允许请求,返回成功响应 return ResponseEntity.ok("请求成功!"); } else { // 拒绝请求,返回限流响应 return ResponseEntity.status(429).body("请求过多,请稍后再试!"); } } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.

6.进阶版利用切面:

复制
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RateLimitAspect { double qps() default 1; // 每秒钟生成令牌的速率 long timeout() default 0; // 尝试获取令牌的超时时间 TimeUnit timeUnit() default TimeUnit.SECONDS; // 超时时间单位 }1.2.3.4.5.6.7.
复制
@Aspect @Component public class ApiRateLimitAspect { private final Map<String, RateLimiter> rateLimiters = new ConcurrentHashMap<>(); @Before("@annotation(RateLimitAspect)") public void limit(JoinPoint joinPoint, RateLimitAspect rateLimitAspect) { String methodName = joinPoint.getSignature().toLongString(); double qps = rateLimitAspect.qps(); RateLimiter limiter = rateLimiters.computeIfAbsent(methodName, k -> RateLimiter.create(qps)); long timeout = rateLimitAspect.timeout(); TimeUnit timeUnit = rateLimitAspect.timeUnit(); if (timeout > 0) { if (!limiter.tryAcquire(timeout, timeUnit)) { throw new RuntimeException("API rate limit exceeded"); } } else { if (!limiter.tryAcquire()) { throw new RuntimeException("API rate limit exceeded"); } } } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.

THE END
本站服务器由亿华云赞助提供-企业级高防云服务器