在构建高并发、高可用性的现代应用时,限流(Rate Limiting)是不可或缺的一环。它能够保护后端服务免受过载,防止恶意攻击,并确保系统资源的公平分配。对于分布式系统而言,限流的挑战在于如何跨多个实例同步和管理限流状态。Redis因其高性能和原子操作特性,成为实现分布式限流的理想选择。
常见的限流算法包括漏桶(Leaky Bucket)和令牌桶(Token Bucket)。令牌桶算法因其允许一定程度的突发流量而更受青睐。本文将围绕令牌桶算法,并结合“滚动窗口”的概念来构建限流器。
核心需求点:
在Java生态中,有多种限流库和实现方式。例如,一些基于Redis的Lua脚本实现,或像Redisson这样的综合性框架。然而,对于本文所关注的“滚动窗口”和“回退时间”这两个核心需求,Bucket4j库提供了非常强大和灵活的支持。
立即学习“Java免费学习笔记(深入)”;
Bucket4j是一个Java的令牌桶限流库,它支持多种持久化方式,包括内存、JCache、Redis等。其强大的API能够提供详细的限流结果,包括剩余令牌数和下次可重试的时间。
初学者可能会误解Bucket4j不提供回退时间,但实际上,它通过 ConsumptionProbe 对象提供了非常详细的诊断信息,其中包括 getNanosToWaitForRefill() 方法,这正是我们所需的回退时间。
虽然Bucket4j是令牌桶算法的实现,但其“令牌填充速率”和“桶容量”的组合,可以有效地模拟出对“滚动窗口”的限制效果。例如,一个每秒填充10个令牌,容量为100个令牌的桶,意味着在过去一段时间内,平均每秒最多处理10个请求,且在短时间内最多允许100个请求的突发。
以下是一个基于Redis和Bucket4j实现分布式限流的示例。我们将使用Jedis作为Redis客户端的集成。
首先,在pom.xml中添加Bucket4j及其Jedis集成依赖:
<dependencies> <!-- Bucket4j Core --> <dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-core</artifactId> <version>8.1.1</version> <!-- 请使用最新稳定版本 --> </dependency> <!-- Bucket4j Jedis integration --> <dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-redis</artifactId> <version>8.1.1</version> <!-- 确保与bucket4j-core版本一致 --> </dependency> <!-- Jedis client --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>4.3.1</version> <!-- 请使用最新稳定版本 --> </dependency> </dependencies>
接下来,我们将配置一个基于Redis的限流器。这里我们创建一个每秒允许10个请求,最大突发容量为20个请求的限流器。
import io.github.bucket4j.Bandwidth; import io.github.bucket4j.Bucket; import io.github.bucket4j.Bucket4j; import io.github.bucket4j.ConsumptionProbe; import io.github.bucket4j.redis.jedis.JedisBasedProxyManager; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import java.time.Duration; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; public class DistributedRateLimiter { private static JedisPool jedisPool; private static JedisBasedProxyManager proxyManager; public static void initRedis(String redisHost, int redisPort) { JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(10); // 最大连接数 poolConfig.setMaxIdle(5); // 最大空闲连接数 jedisPool = new JedisPool(poolConfig, redisHost, redisPort); proxyManager = new JedisBasedProxyManager(jedisPool); } /** * 获取或创建限流桶。 * 每个唯一的key对应一个独立的限流桶。 * * @param key 限流的唯一标识符(例如:用户ID, IP地址, API路径) * @param capacity 令牌桶容量 * @param refillTokens 每次填充的令牌数 * @param refillPeriod 令牌填充周期 * @return 配置好的限流桶 */ public static Bucket getOrCreateLimiterBucket(String key, long capacity, long refillTokens, Duration refillPeriod) { // 定义限流带宽 Bandwidth limit = Bandwidth.builder() .capacity(capacity) // 令牌桶容量 .refillGreedy(refillTokens, refillPeriod) // 令牌填充策略:每 refillPeriod 填充 refillTokens 个令牌 .build(); // 使用proxyManager获取或创建分布式桶 // key是限流器的唯一标识,Supplier是当key不存在时如何创建桶的定义 return Bucket4j.builder() .addLimit(limit) .build(proxyManager, key); } public static void main(String[] args) throws InterruptedException { // 初始化Redis连接池 initRedis("localhost", 6379); // 假设Redis运行在本地默认端口 String userId = "user:123"; // 为该用户设置限流:每秒10个请求,最大突发20个请求 Bucket userBucket = getOrCreateLimiterBucket(userId, 20, 10, Duration.ofSeconds(1)); System.out.println("--- 模拟用户请求 ---"); for (int i = 0; i < 30; i++) { // 尝试消费一个令牌 ConsumptionProbe probe = userBucket.tryConsumeAndReturnRemaining(1); if (probe.isConsumed()) { // 请求成功 System.out.printf("请求 %d 成功!剩余令牌:%d%n", i + 1, probe.getRemainingTokens()); } else { // 请求被拒绝 long nanosToWaitForRefill = probe.getNanosToWaitForRefill(); long millisToWaitForRefill = TimeUnit.NANOSECONDS.toMillis(nanosToWaitForRefill); System.err.printf("请求 %d 被拒绝!需要等待 %d 毫秒后重试。当前剩余令牌:%d%n", i + 1, millisToWaitForRefill, probe.getRemainingTokens()); // 模拟客户端等待回退时间 if (millisToWaitForRefill > 0) { Thread.sleep(millisToWaitForRefill); } } // 每次请求间隔模拟 Thread.sleep(50); } // 关闭Redis连接池 jedisPool.close(); } }
通过模拟客户端在请求被拒绝后等待millisToWaitForRefill,我们实现了有效的回退机制,避免了无效的重试,保护了服务。
本文详细介绍了如何在Java中使用Bucket4j和Redis构建一个强大的分布式限流器,重点解决了如何实现类似“滚动窗口”的行为以及获取“回退时间”的需求。Bucket4j的ConsumptionProbe机制提供了丰富的信息,使得实现智能的限流策略和友好的客户端交互成为可能。通过合理配置和应用,开发者可以有效保护系统,提升服务稳定性。
以上就是基于Redis和Bucket4j的Java分布式限流器:实现滚动窗口与回退机制的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号