在分布式系统和高并发场景中,限流是一个非常常见且重要的需求。以下是一些常用的限流工具和库,包括它们的特点和使用场景:
1. Guava RateLimiter
Google 的 Guava 库中的 RateLimiter
是一个简单且高效的限流工具,适用于单节点应用。
优点:
- 易于使用
- 高效
缺点:
- 仅适用于单节点环境,不支持分布式限流
示例代码:
import com.google.common.util.concurrent.RateLimiter;
public class GuavaRateLimiterExample {
public static void main(String[] args) {
// 创建一个每秒允许10个请求的RateLimiter
RateLimiter rateLimiter = RateLimiter.create(10.0);
for (int i = 0; i < 20; i++) {
// 尝试获取一个许可
if (rateLimiter.tryAcquire()) {
System.out.println("Acquired permit " + (i + 1));
// 执行限流操作
} else {
System.out.println("Could not acquire permit " + (i + 1));
}
}
}
}
2. Redis + Lua 脚本
使用 Redis 和 Lua 脚本可以实现分布式限流,因为 Redis 本身是一个高性能的分布式缓存和存储系统。
优点:
- 支持分布式
- 高性能
缺点:
- 实现复杂度较高,需要编写和维护 Lua 脚本
示例代码(Lua脚本):
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local current = tonumber(redis.call('get', key) or "0")
if current + 1 > limit then
return 0
else
redis.call("INCRBY", key, 1)
redis.call("EXPIRE", key, 1)
return 1
end
Java代码调用Lua脚本:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class RedisRateLimiter {
private JedisPool jedisPool;
public RedisRateLimiter(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
public boolean tryAcquire(String key, int limit) {
try (Jedis jedis = jedisPool.getResource()) {
String luaScript = "..."; // 上述Lua脚本内容
Object result = jedis.eval(luaScript, 1, key, String.valueOf(limit));
return result.equals(1L);
}
}
}
3. Redisson RRateLimiter
Redisson 是一个基于 Redis 的 Java 客户端,提供了多种分布式数据结构和服务,其中包括 RRateLimiter
用于分布式限流。
优点:
- 支持分布式
- 使用方便,集成良好
缺点:
- 依赖 Redis
示例代码:
import org.redisson.Redisson;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
public class RedissonRateLimiterExample {
public static void main(String[] args) {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
RRateLimiter rateLimiter = redisson.getRateLimiter("myRateLimiter");
rateLimiter.trySetRate(RRateLimiter.RateType.OVERALL, 5, 1, RateIntervalUnit.SECONDS);
for (int i = 0; i < 10; i++) {
if (rateLimiter.tryAcquire()) {
System.out.println("Acquired permit " + (i + 1));
} else {
System.out.println("Could not acquire permit " + (i + 1));
}
}
redisson.shutdown();
}
}
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
</dependency>
spring:
redis:
redisson:
config: |
singleServerConfig:
address: redis://xxxxx:6383
connectionPoolSize: 30
connectionMinimumIdleSize: 30
password: xxx
database: 0
connectTimeout: 1000
timeout: 500
retryAttempts: 3
retryInterval: 1000
threads: 10
nettyThreads: 10
transportMode: NIO
@Component
@Configuration
public class XXXControlConfig {
@Autowired
private RedissonClient redissonClient;
@Autowired
private xxxControlProperties xxxControlProperties;
/**
* 拆章滑动窗口
*/
@Bean
public RRateLimiter rateLimiter() {
RRateLimiter rateLimiter = redissonClient.getRateLimiter("xxx");
rateLimiter.trySetRate(RateType.OVERALL,
flowControlProperties.getRate().getInterval(),
flowControlProperties.getRate().getPermits(),
RateIntervalUnit.MINUTES);
return rateLimiter;
}
/**
* 试听最大并发数
*/
@Bean
public RSemaphore semaphore() {
RSemaphore semaphore = redissonClient.getSemaphore("xxxxx");
semaphore.trySetPermits(flowControlProperties.getSemaphore().getPermits());
return semaphore;
}
@Autowired
private RRateLimiter rateLimiter;
if(flowControlProperties.getRate().isEnabled() && !rateLimiter.tryAcquire()) {
throw new SystemException(Code, "系统繁忙,请稍后再试");
}
4. Bucket4j
Bucket4j 是一个 Java 限流库,提供了灵活的令牌桶算法实现,可用于本地或分布式限流(通过第三方存储如 Hazelcast)。
优点:
- 灵活
- 支持多种存储后端
缺点:
- 配置相对复杂
示例代码:
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Bucket4j;
import io.github.bucket4j.Refill;
import java.time.Duration;
public class Bucket4jExample {
public static void main(String[] args) {
Bandwidth limit = Bandwidth.classic(10, Refill.greedy(10, Duration.ofSeconds(1)));
Bucket bucket = Bucket4j.builder().addLimit(limit).build();
for (int i = 0; i < 20; i++) {
if (bucket.tryConsume(1)) {
System.out.println("Acquired permit " + (i + 1));
} else {
System.out.println("Could not acquire permit " + (i + 1));
}
}
}
}
这些工具各有优缺点,可以根据你的具体需求选择合适的限流工具。如果你需要分布式限流,推荐使用 Redis 相关的解决方案,如 Redisson 或者自定义 Lua 脚本。如果是单节点应用,Guava 的 RateLimiter
或 Bucket4j 都是不错的选择。