文章目录
1. 引言:限流背景与 Bucket4j 项目概述
在微服务与高并发系统中,合理地限制请求速率能够保护后端服务不被洪水般的请求压垮,平滑流量并保障系统可用性。Bucket4j 是一个基于 Java 的令牌桶(Token Bucket)限流库,支持内存与多种分布式存储后端(如 Redis、Hazelcast、JCache 等),并且提供丰富的 API,让开发者能灵活定义限流策略与集成方式。
Bucket4j 的核心优势在于:
- 纯 Java 实现,无需引入复杂的代理或外部依赖
- 灵活的令牌桶配置:支持定速、突发模式、带时间窗口的限流
- 分布式后端支持:基于 Redis、Hazelcast、Caffeine 等实现共享限流
- 同步 & 异步 API:满足不同场景下对性能的要求
2. 核心概念:令牌桶算法与 Bucket4j 组件
- 令牌桶(Token Bucket):维护一个“桶”,桶中存放令牌,令牌按固定速率生成。当请求到来时,需要消耗一定数量的令牌,若桶空则拒绝请求或等待。
Bandwidth
:代表一个限流规则,包括容量(maxTokens)、填充速率(refillTokens/refillPeriod)等。Bucket
:对应一个具体的令牌桶实例,由一个或多个Bandwidth
组成。- 时间度量器(TimeMeter):Bucket4j 内部使用,可替换为自定义实现以适配不同环境。
3. 快速入门:Maven 依赖与基础配置
在 Maven 项目中引入核心依赖:
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
<version>8.3.0</version>
</dependency>
若使用 Redis 后端,还需引入:
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-redis-extension</artifactId>
<version>8.3.0</version>
</dependency>
4. 基本用法:创建桶、消费令牌、检查剩余容量
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Bucket4j;
import java.time.Duration;
public class RateLimiterDemo {
public static void main(String[] args) {
// 定义限流规则:每秒生成 10 个令牌,桶容量最大为 20
Bandwidth limit = Bandwidth.simple(10, Duration.ofSeconds(1))
.withInitialTokens(20);
// 创建令牌桶
Bucket bucket = Bucket4j.builder()
.addLimit(limit)
.build();
// 尝试消费令牌
if (bucket.tryConsume(1)) {
// 成功消费一个令牌,允许请求
System.out.println("请求允许,剩余令牌:" + bucket.getAvailableTokens());
} else {
// 消费失败,限流
System.out.println("请求被限流!");
}
}
}
tryConsume(n)
:立即尝试消耗 n 个令牌,返回布尔结果。consume(n)
:不足时阻塞等待。
5. 高级功能:多种填充策略、异步模式、分布式存储
多个 Bandwidth
组合
Bucket bucket = Bucket4j.builder()
.addLimit(Bandwidth.simple(5, Duration.ofSeconds(1))) // 短期限流
.addLimit(Bandwidth.simple(50, Duration.ofMinutes(1))) // 长期限流
.build();
异步 API
bucket.asAsync().tryConsume(1)
.thenAccept(consumed -> {
if (consumed) System.out.println("异步限流通过");
else System.out.println("异步限流拒绝");
});
分布式后端示例(Redis)
import io.github.bucket4j.redis.RedisBucketBuilder;
import io.github.bucket4j.redis.redisson.cas.RedissonBasedProxyManager;
import org.redisson.api.RedissonClient;
RedissonClient client = ...; // 注入或创建 RedissonClient
RedissonBasedProxyManager<String> proxyManager = new RedissonBasedProxyManager<>(client);
Bucket bucket = proxyManager.builderForKey("user:1234:bucket")
.withDefaultBandwidth(Bandwidth.simple(10, Duration.ofSeconds(1)))
.build();
通过分布式代理管理器,可以在多实例环境中共享同一令牌桶,实现全局限流。
6. 与 Spring Boot 集成:过滤器与注解实现
方式一:Servlet 过滤器
@Component
public class RateLimitFilter extends OncePerRequestFilter {
private final Bucket bucket = Bucket4j.builder()
.addLimit(Bandwidth.simple(100, Duration.ofMinutes(1)))
.build();
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
throws ServletException, IOException {
if (bucket.tryConsume(1)) {
chain.doFilter(req, res);
} else {
res.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
res.getWriter().write("Too many requests");
}
}
}
方式二:自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
int tokens() default 1;
long replenishRate() default 10;
long periodSeconds() default 1;
}
@Aspect
@Component
public class RateLimitAspect {
@Around("@annotation(rateLimit)")
public Object around(ProceedingJoinPoint pjp, RateLimit rateLimit) throws Throwable {
Bucket bucket = Bucket4j.builder()
.addLimit(Bandwidth.simple(rateLimit.replenishRate(), Duration.ofSeconds(rateLimit.periodSeconds())))
.build();
if (bucket.tryConsume(rateLimit.tokens())) {
return pjp.proceed();
} else {
throw new ResponseStatusException(HttpStatus.TOO_MANY_REQUESTS, "请求频率过高");
}
}
}
7. 性能与注意事项
- 线程安全:Bucket4j 的核心实现是线程安全的,单机模式下性能开销极低。
- 内存 vs. 分布式:本地桶速度最快;若需要多实例共享状态,可使用 Redis/Hazelcast 后端。
- 状态持久化:分布式后端需注意网络延迟与故障切换策略。
- 容量计算:合理设置
initialTokens
与bandwidth
,避免过度积累或瞬时突发耗尽。
8. 应用场景
- API 网关限流:保护下游服务,防止恶意或意外的高并发。
- 登录接口防刷:限制同一用户/IP 的请求频率。
- 批量任务节流:控制批量处理任务发送速率,平滑资源使用。
9. 与其他限流库的比较
Bucket4j 与其他常见的限流库(如 Guava RateLimiter 和 Resilience4j)相比,具有以下优势:
- 灵活性:支持多种限流算法和动态配置。
- 分布式支持:原生支持分布式环境,适合微服务架构。
- 高性能:专为高并发场景设计,延迟低、吞吐量高。
- 易用性:API 简洁,学习曲线平缓。
特性 | Bucket4j | Guava RateLimiter | Resilience4j |
---|---|---|---|
分布式支持 | 是 | 否 | 否 |
动态配置 | 是 | 否 | 是 |
多种算法支持 | 是 | 否 | 是 |
易于集成 | 高 | 中 | 高 |
10. 完整的 Spring Boot 限流 Starter
spring-boot-rate-limiter-starter/
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── ratelimiter
│ │ │ ├── starter
│ │ │ │ ├── RateLimiterAutoConfiguration.java
│ │ │ │ ├── RateLimit.java
│ │ │ │ ├── RateLimitAspect.java
│ │ │ │ └── RateLimitProperties.java
│ │ └── resources
│ │ ├── META-INF
│ │ │ └── spring.factories
│ │ └── application.yml
└── example-usage
└── src
└── main
└── java
└── com
└── example
└── demo
├── DemoApplication.java
└── DemoController.java
1. pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" ...>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spring-boot-rate-limiter-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.4</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
<version>8.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2. RateLimitProperties.java
package com.example.ratelimiter.starter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;
@ConfigurationProperties(prefix = "ratelimiter")
public class RateLimitProperties {
private long replenishRate = 10;
private Duration period = Duration.ofSeconds(1);
private long capacity = 20;
// getters & setters
}
3. RateLimit.java (注解)
package com.example.ratelimiter.starter;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {
long capacity() default -1;
long replenishRate() default -1;
String period() default ""; // ISO-8601 duration
int tokens() default 1;
}
4. RateLimitAspect.java
package com.example.ratelimiter.starter;
import io.github.bucket4j.*;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
@Aspect
@Component
@ConditionalOnProperty(prefix = "ratelimiter", name = "enabled", havingValue = "true", matchIfMissing = true)
public class RateLimitAspect {
private final RateLimitProperties properties;
private final Map<String, Bucket> cache = new ConcurrentHashMap<>();
public RateLimitAspect(RateLimitProperties properties) {
this.properties = properties;
}
@Around("@annotation(com.example.ratelimiter.starter.RateLimit) || @within(com.example.ratelimiter.starter.RateLimit)")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
RateLimit annotation = AnnotationUtils.findAnnotation(
pjp.getSignature().getDeclaringType(), RateLimit.class);
if (annotation == null) {
annotation = AnnotationUtils.findAnnotation(
((org.aspectj.lang.reflect.MethodSignature) pjp.getSignature()).getMethod(), RateLimit.class);
}
String key = pjp.getSignature().toShortString();
Bucket bucket = cache.computeIfAbsent(key, k -> buildBucket(annotation));
if (bucket.tryConsume(getTokens(annotation))) {
return pjp.proceed();
}
throw new IllegalStateException("Too many requests");
}
private Bucket buildBucket(RateLimit rl) {
long capacity = rl.capacity() > 0 ? rl.capacity() : properties.getCapacity();
long replenish = rl.replenishRate() > 0 ? rl.replenishRate() : properties.getReplenishRate();
Duration period = !rl.period().isEmpty() ? Duration.parse(rl.period()) : properties.getPeriod();
Bandwidth limit = Bandwidth.classic(capacity, Refill.greedy(replenish, period));
return Bucket4j.builder().addLimit(limit).build();
}
private int getTokens(RateLimit rl) {
return rl.tokens();
}
}
5. RateLimiterAutoConfiguration.java
package com.example.ratelimiter.starter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass(Bucket4j.class)
@EnableConfigurationProperties(RateLimitProperties.class)
@ConditionalOnProperty(prefix = "ratelimiter", name = "enabled", havingValue = "true", matchIfMissing = true)
public class RateLimiterAutoConfiguration {
// 自动装配切面和属性
}
6. spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.ratelimiter.starter.RateLimiterAutoConfiguration
7. application.yml (Starter 默认配置)
ratelimiter:
enabled: true
replenishRate: 5
period: PT1S
capacity: 10
示例项目 (example-usage)
DemoApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
DemoController.java
package com.example.demo;
import com.example.ratelimiter.starter.RateLimit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@GetMapping("/hello")
@RateLimit(tokens = 1, capacity = 3, replenishRate = 1, period = "PT1S")
public String hello() {
return "Hello, world!";
}
}
上述脚手架包含了一个可发布为 Spring Boot Starter 的完整示例:
pom.xml
:定义了核心依赖(Spring Boot、Bucket4j、AspectJ 和自动配置)。RateLimitProperties
:用于读取application.yml
中的限流参数。@RateLimit
注解:可标注在类或方法上,灵活覆盖全局属性。RateLimitAspect
:通过 AOP 拦截带注解的请求,基于 Bucket4j 实现令牌桶限流。RateLimiterAutoConfiguration
+spring.factories
:实现自动装配。- 默认配置 (
application.yml
):提供全局限流开关与默认参数。 - 示例项目 (
example-usage
):展示在 Spring Boot 应用中如何引入并使用 Starter。
可以将 spring-boot-rate-limiter-starter
安装到私服或本地仓库,并在任何 Spring Boot 项目中添加依赖:
<dependency>
<groupId>com.example</groupId>
<artifactId>spring-boot-rate-limiter-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
然后在控制器方法上使用 @RateLimit
即可轻松接入限流功能。
发布
发布到 Maven 本地仓库
在 spring-boot-rate-limiter-starter
根目录下执行:
mvn clean install
- 作用:将
0.0.1-SNAPSHOT
版本安装到~/.m2/repository/com/example/spring-boot-rate-limiter-starter/
发布到私服
-
在
pom.xml
中添加distributionManagement
<distributionManagement> <repository> <id>private-repo</id> <url>https://nexus.example.com/repository/maven-releases/</url> </repository> <snapshotRepository> <id>private-snapshots</id> <url>https://nexus.example.com/repository/maven-snapshots/</url> </snapshotRepository> </distributionManagement>
-
在
~/.m2/settings.xml
中配置凭据<servers> <server> <id>private-repo</id> <username>deploy</username> <password>您的密码</password> </server> <server> <id>private-snapshots</id> <username>deploy</username> <password>您的密码</password> </server> </servers>
-
执行发布
mvn clean deploy
– 将 release 和 snapshot 同步到私服对应仓库。
在应用中添加依赖
在 example-usage/pom.xml
中,添加:
<repositories>
<repository>
<id>private-repo</id>
<url>https://nexus.example.com/repository/maven-releases/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>spring-boot-rate-limiter-starter</artifactId>
<version>0.0.1-SNAPSHOT</version> <!-- 或发布后的正式版本 -->
</dependency>
</dependencies>
然后重新启动示例应用,@RateLimit
即可生效。
11. 小结与参考链接
Bucket4j 提供了一个灵活、可扩展且性能优秀的 Java 限流方案。通过丰富的 API 与多种后端适配,能够满足从单机到分布式、多速率到混合限流等多种需求。
- 官方仓库:https://github.com/bucket4j/bucket4j
- 官方网站:https://bucket4j.com