Java中的速率限制:从理论到实践实现
速率限制(Rate Limiting)是现代分布式系统中保护和管理负载的关键机制之一。在Java中,这个问题既可以通过内置工具解决,也可以借助专门的库。本文将介绍主要算法、流行库,并提供可运行的代码示例。
为什么需要速率限制?
速率限制可以实现:
- 保护API免受滥用(DDoS攻击、密码暴力破解)
- 均匀分配后端负载
- 遵守服务等级协议(SLA)
- 防止超出外部服务的配额
主要的速率限制算法
在选择库之前,了解底层算法很重要:
1. 令牌桶(Bucket4j, Guava)
经典算法。令牌以固定速率添加到“桶”中。每个请求消耗一个或多个令牌。如果没有令牌,请求被拒绝。
// 伪代码示例class TokenBucket { private int tokens; private long lastRefillTime; private final int capacity; private final double refillRate; // 每秒令牌数
synchronized boolean tryConsume() { refill(); if (tokens > 0) { tokens--; return true; } return false; }}2. 滑动窗口日志
存储最近请求的时间戳。每个新请求会删除超出窗口的时间戳,并检查是否超过限制。精确,但需要内存存储时间戳。
3. 固定窗口计数器
固定时间间隔内的简单计数器(例如每分钟100个请求)。每分钟重置。容易在窗口边界出现“峰值”负载。
流行的Java速率限制库
1. Bucket4j
Bucket4j 是一个成熟的库,完整实现了令牌桶算法。支持通过Redis、Hazelcast、JCache实现分布式场景。提供Java 8+ API。
安装(Maven):
<dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-core</artifactId> <version>8.7.0</version></dependency>使用示例:
import io.github.bucket4j.*;import java.time.Duration;
public class Bucket4jExample { public static void main(String[] args) { // 创建桶:10个令牌,每秒补充1个令牌 Bandwidth limit = Bandwidth.simple(10, Duration.ofSeconds(1)); Bucket bucket = Bucket4j.builder().addLimit(limit).build();
for (int i = 0; i < 15; i++) { if (bucket.tryConsume(1)) { System.out.println("请求 #" + i + " 已执行"); } else { System.out.println("请求 #" + i + " 被拒绝(超出限制)"); } } }}2. Resilience4j RateLimiter
属于流行的容错框架的一部分。基于SemaphoreBasedRateLimiter或AtomicRateLimiter实现。易于与Spring Boot集成。
配置示例:
import io.github.resilience4j.ratelimiter.*;import java.time.Duration;
public class Resilience4jExample { public static void main(String[] args) { RateLimiterConfig config = RateLimiterConfig.custom() .limitForPeriod(5) // 5个请求 .limitRefreshPeriod(Duration.ofSeconds(1)) // 每秒刷新 .timeoutDuration(Duration.ofMillis(100)) .build();
RateLimiter rateLimiter = RateLimiter.of("myService", config);
for (int i = 0; i < 8; i++) { Runnable task = R