基于Redission 实现限流1-令牌桶算法
概述:
限流是一种常用的系统保护技术,其本质是控制进入系统的请求速率,以确保系统的稳定性和可靠性。限流可以防止系统因为超负荷的请求而崩溃,从而保证系统对正常请求的响应能力。 限流的原理基于流量控制理论,可以通过多种算法实现,包括但不限于以下几种: 限流是一种控制网络流量或服务请求速率的技术,以防止系统超载并确保服务的可用性和稳定性。
常见的限流算法:
-
固定窗口算法(Fixed Window Counter):
- 也称为计数器算法。
- 将时间分割成固定长度的窗口,每个窗口内都有一个独立的计数器。
- 在每个窗口期间,系统跟踪请求的数量,并在达到预设的限制时拒绝额外的请求。
- 缺点是在窗口切换时可能出现两倍于限制的请求被允许的情况(窗口临界问题)。
-
滑动窗口日志算法(Sliding Window Log):
- 记录每个请求的时间戳。
- 当新请求到来时,移除超出当前时间窗口的所有请求记录,然后检查剩余记录的数量。
- 如果请求数量小于限制,则允许新请求并记录其时间戳;如果超过限制,则拒绝。
- 较固定窗口算法更平滑,但需要存储更多的数据。
-
滑动窗口计数器算法(Sliding Window Counter):
- 结合了固定窗口和滑动窗口日志的特点。
- 使用多个计数器来代表不同的时间段,并根据当前时间计算出一个加权总计数。
- 这种方法提供了比固定窗口更平滑的限流,但实现起来比较复杂。
-
漏桶算法(Leaky Bucket):
- 将请求想象为流入桶中的水,桶以固定速率漏水(处理请求)。
- 如果桶满了(达到容量限制),新流入的水(请求)会被丢弃。
- 无论输入流量如何变化,输出流量始终保持恒定。
-
令牌桶算法(Token Bucket):
- 与漏桶算法类似,但更灵活。
- 令牌(token)以固定速率放入桶中,每个请求需要消耗一个令牌才能被处理。
- 如果桶中有足够的令牌,请求立即被处理;如果没有,请求可以等待令牌或被拒绝。
- 允许突发流量直到令牌耗尽,然后恢复到固定速率。
-
自适应限流(Adaptive Throttling):
- 根据系统的当前性能指标(如响应时间、队列长度等)动态调整限流阈值。
- 在系统负载较低时放宽限制,在负载较高时加强限流。
- 这种方法需要复杂的监控和调整机制。
具体实现限流的方法有很多,可以在不同的层次上进行,例如应用层、网络层、数据库层等。
常见的限流实现方式:
- 中间件/服务网关限流: 使用如 Nginx、Apache、Zuul、Spring Cloud Gateway 等中间件或服务网关提供的限流功能。
- 负载均衡器限流: 在负载均衡器层面设置限流规则,例如 AWS ELB、F5 等。
- 编程语言或框架提供的限流库: 如 Guava RateLimiter、Resilience4j、Sentinel 等。
- 自定义限流组件: 根据业务需求自己实现限流逻辑。
令牌桶算法实现:
本文将重点介绍基于Redission 实现令牌桶算法
源码:
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
/**
* @Author derek_smart
* @Date 202/5/12 8:01
* @Description 实现基于令牌桶算法的限流器
* <p>
*/
public class RedissonTokenBucketRateLimiter {
private final RRateLimiter rateLimiter;
public RedissonTokenBucketRateLimiter(RedissonClient redissonClient, String name, int rate, int rateInterval) {
// 获取或创建一个名为 name 的 RRateLimiter 对象
rateLimiter = redissonClient.getRateLimiter(name);
// 初始化 RRateLimiter 配置
// 设置每 rateInterval 时间单位生成 rate 个令牌
rateLimiter.trySetRate(RateType.OVERALL, rate, rateInterval, RateIntervalUnit.SECONDS);
}
public boolean tryAcquire() {
// 尝试获取一个令牌,如果获取成功则返回 true,否则返回 false
return rateLimiter.tryAcquire();
}
public boolean tryAcquire(int permits) {
// 尝试获取指定数量的令牌,如果获取成功则返回 true,否则返回 false
return rateLimiter.tryAcquire(permits);
}
public void acquire() throws InterruptedException {
// 阻塞直到获取一个令牌
rateLimiter.acquire();
}
public void acquire(int permits) throws InterruptedException {
// 阻塞直到获取指定数量的令牌
rateLimiter.acquire(permits);
}
}
下面是该类及其方法的介绍:
类结构
- 私有成员 rateLimiter
:这是 RRateLimiter
类型的一个实例,它是 Redisson 提供的令牌桶限流器的具体实现。
- 构造函数:在构造函数中,通过 Redisson 客户端 redissonClient
创建或获取一个命名为 name
的 RRateLimiter
对象,并使用 trySetRate
方法初始化限流器的配置。
方法
- tryAcquire()
:尝试从令牌桶中获取一个令牌。如果令牌桶中有可用的令牌,则返回 true
,表示成功获取令牌;如果没有令牌可用,则返回 false
,表示此时不能处理请求,达到限流效果。
- tryAcquire(int permits)
:尝试从令牌桶中获取指定数量 permits
的令牌。如果可以获取到足够的令牌,则返回 true
;如果令牌不足,则返回 false
。
- acquire()
:阻塞当前线程直到从令牌桶中获取到一个令牌。如果令牌桶中没有可用的令牌,当前线程将等待直到有令牌变得可用。
- acquire(int permits)
:阻塞当前线程直到从令牌桶中获取到指定数量 permits
的令牌。如果令牌不足,当前线程将等待直到足够的令牌可用。
限流器配置
在构造函数中,trySetRate
方法用于设置限流器的关键参数:
- RateType.OVERALL
:指定限流器的类型。OVERALL
表示整体的限流策略,即不区分调用者,所有请求共享相同的令牌桶。
- rate
:表示每个 rateInterval
时间单位内生成的令牌数。
- rateInterval
:表示生成令牌的时间间隔。
- RateIntervalUnit.SECONDS
:表示时间间隔的单位是秒。
使用场景
此类可以用于控制对资源的访问速率,以避免过载或崩溃。例如,在 Web 应用程序中,可以使用它来限制对 API 端点的请求速率,或者在微服务架构中限制服务之间的调用速率。它特别适用于分布式系统,因为 Redisson 的 RRateLimiter
是基于 Redis 的,所以状态是跨多个实例共享的。
使用 RedissonTokenBucketRateLimiter
可以很容易地在 Java 应用程序中实现分布式限流,而不需要担心单点故障或复杂的并发场景。
限流的实现通常需要考虑的因素包括请求的均匀性、突发性、系统的处理能力、业务的优先级等。正确配置和实施限流策略有助于提高系统的健壮性和服务的质量。
RedissonTokenBucketRateLimiter 总结
RedissonTokenBucketRateLimiter
是一个基于 Redisson 客户端库的限流器类,它实现了令牌桶算法。这个类提供了一种在 Java 应用程序中轻松实现分布式限流的方法。以下是该类的主要特点:
- 分布式限流:由于它基于 Redis,因此可以在多个应用实例之间共享限流状态,适合分布式系统。
- 高并发性:Redisson 本身支持高并发操作,因此 RedissonTokenBucketRateLimiter
可以在高并发环境下工作而不会丢失令牌。
- 灵活性:可以动态设置生成令牌的速率和间隔,适应不同的流量需求。
- 简单易用:提供了简洁的 API 来获取和释放令牌,使得集成和使用变得非常简单。
- 阻塞和非阻塞模式:支持非阻塞的 tryAcquire
方法和阻塞的 acquire
方法,以便于在不同的场景下使用。
使用 RedissonTokenBucketRateLimiter
时,需要注意的是:
- 应当确保限流器的配置(如速率和间隔)适合你的应用程序需求。 - 在分布式环境中,限流器的名字应该是全局唯一的。 - 应用程序关闭时,需要关闭 Redisson 客户端以释放资源。
Redission 实现限流令牌桶算法总结
Redisson 提供的 RRateLimiter
是一个基于 Redis 的限流组件,它实现了令牌桶算法,并且具有以下优点:
- 分布式和高可用:由于基于 Redis,它天然支持分布式环境,可以在系统的不同组件之间共享限流器。 - 持久化:令牌桶的状态存储在 Redis 中,这意味着即使应用程序重启,限流器的状态也不会丢失。 - 原子性:Redisson 通过使用 Redis 命令来保证操作的原子性,避免了并发环境下的竞态条件。 - 易于监控:可以使用 Redis 的监控工具来观察和调试限流器的状态和性能。
为了有效利用 Redisson 的 RRateLimiter
应该:
- 对 Redis 的性能和可用性进行监控,因为它是限流器的关键依赖。
- 根据实际情况调整令牌发放速率和容量,以满足业务需求并防止资源过载。
- 在实现限流逻辑时,考虑合理的错误处理和失败重试策略。
总的来说,Redisson 的 RRateLimiter
提供了一个强大而灵活的限流解决方案,适合需要在分布式系统中实现限流的场景。通过 RedissonTokenBucketRateLimiter
类的封装,开发者可以更加便捷地在 Java 应用中集成和使用这一功能。
转载自:https://juejin.cn/post/7370356693593292800