基于Redission 实现限流1-令牌桶算法

概述:

限流是一种常用的系统保护技术,其本质是控制进入系统的请求速率,以确保系统的稳定性和可靠性。限流可以防止系统因为超负荷的请求而崩溃,从而保证系统对正常请求的响应能力。 限流的原理基于流量控制理论,可以通过多种算法实现,包括但不限于以下几种: 限流是一种控制网络流量或服务请求速率的技术,以防止系统超载并确保服务的可用性和稳定性。

常见的限流算法:

  1. 固定窗口算法(Fixed Window Counter):

    • 也称为计数器算法。
    • 将时间分割成固定长度的窗口,每个窗口内都有一个独立的计数器。
    • 在每个窗口期间,系统跟踪请求的数量,并在达到预设的限制时拒绝额外的请求。
    • 缺点是在窗口切换时可能出现两倍于限制的请求被允许的情况(窗口临界问题)。
  2. 滑动窗口日志算法(Sliding Window Log):

    • 记录每个请求的时间戳。
    • 当新请求到来时,移除超出当前时间窗口的所有请求记录,然后检查剩余记录的数量。
    • 如果请求数量小于限制,则允许新请求并记录其时间戳;如果超过限制,则拒绝。
    • 较固定窗口算法更平滑,但需要存储更多的数据。
  3. 滑动窗口计数器算法(Sliding Window Counter):

    • 结合了固定窗口和滑动窗口日志的特点。
    • 使用多个计数器来代表不同的时间段,并根据当前时间计算出一个加权总计数。
    • 这种方法提供了比固定窗口更平滑的限流,但实现起来比较复杂。
  4. 漏桶算法(Leaky Bucket):

    • 将请求想象为流入桶中的水,桶以固定速率漏水(处理请求)。
    • 如果桶满了(达到容量限制),新流入的水(请求)会被丢弃。
    • 无论输入流量如何变化,输出流量始终保持恒定。
  5. 令牌桶算法(Token Bucket):

    • 与漏桶算法类似,但更灵活。
    • 令牌(token)以固定速率放入桶中,每个请求需要消耗一个令牌才能被处理。
    • 如果桶中有足够的令牌,请求立即被处理;如果没有,请求可以等待令牌或被拒绝。
    • 允许突发流量直到令牌耗尽,然后恢复到固定速率。
  6. 自适应限流(Adaptive Throttling):

    • 根据系统的当前性能指标(如响应时间、队列长度等)动态调整限流阈值。
    • 在系统负载较低时放宽限制,在负载较高时加强限流。
    • 这种方法需要复杂的监控和调整机制。

具体实现限流的方法有很多,可以在不同的层次上进行,例如应用层、网络层、数据库层等。

常见的限流实现方式:

  • 中间件/服务网关限流: 使用如 Nginx、Apache、Zuul、Spring Cloud Gateway 等中间件或服务网关提供的限流功能。
  • 负载均衡器限流: 在负载均衡器层面设置限流规则,例如 AWS ELB、F5 等。
  • 编程语言或框架提供的限流库: 如 Guava RateLimiter、Resilience4j、Sentinel 等。
  • 自定义限流组件: 根据业务需求自己实现限流逻辑。

令牌桶算法实现:

本文将重点介绍基于Redission 实现令牌桶算法

源码:

java 复制代码
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 创建或获取一个命名为 nameRRateLimiter 对象,并使用 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 应用中集成和使用这一功能。

相关推荐
希忘auto27 分钟前
详解Redis的常用命令
redis·1024程序员节
suweijie7683 小时前
SpringCloudAlibaba | Sentinel从基础到进阶
java·大数据·sentinel
公贵买其鹿3 小时前
List深拷贝后,数据还是被串改
java
LCG元6 小时前
【面试问题】JIT 是什么?和 JVM 什么关系?
面试·职场和发展
xlsw_7 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
岁月变迁呀7 小时前
Redis梳理
数据库·redis·缓存
神仙别闹7 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭8 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫8 小时前
泛型(2)
java
超爱吃士力架8 小时前
邀请逻辑
java·linux·后端