什么是 Redisson 的 RateLimiter?它在项目中具体如何实现分布式限流?你的限流策略是 什么?

RateLimiter 是 Redisson 基于 Redis 实现的分布式限流组件,底层使用令牌桶算法,能够控制某 个操作或服务在一定时间内的请求频率,保护系统不被过多的请求压垮。
在项目中,考虑到 AI 生成图表是一个耗时且耗费资源的操作,我决定给 AI 生成图表接口增加限 流,具体的策略是:单个用户每秒内最多执行 2 次生成图表操作。
具体的实现方式如下:
1)集中管理限流器:创建一个 RedisLimiterManager 类,集中管理整个项目中所有的限流器,并
提供创建限流器的接口。
2)创建限流器:通过向 redissonClient 传入指定的 key 来创建限流器,每个用户对应的 key 不
同,对应的限流器也不同,从而实现不同用户的独立限流
3)设置限流规则:通过 rateLimiter 的 trySetRate 方法制定限流规则,每秒内最多获取 2 个令牌
4)请求令牌:当用户要执行操作时,会执行对应 rateLimiter 的 tryAcquire 方法尝试获取令牌,
如果能够获取到,可以执行后续操作,否则抛出 TOO_MANY_REQUEST 异常。

说说令牌桶算法 

面对突发流量的时候,我们可以使用令牌桶算法限流。

令牌桶算法原理

  • 有一个令牌管理员,根据限流大小,定速往令牌桶里放令牌。
  • 如果令牌数量满了,超过令牌桶容量的限制,那就丢弃。
  • 系统在接受到一个用户请求时,都会先去令牌桶要一个令牌。如果拿到令牌,那么就处理这个请求的业务逻辑;
  • 如果拿不到令牌,就直接拒绝这个请求。

图片参考出处
作者:捡田螺的小男孩
链接:https://juejin.cn/post/6967742960540581918

实现案例 

引入 Redisson 代码包
<dependency>
 <groupId>org.redisson</groupId>
 <artifactId>redisson</artifactId>
 <version>3.21.3</version>
</dependency>
创建 RedissonConfig 配置类,用于初始化 RedissonClient 对象单例:
@Configuration
@ConfigurationProperties(prefix = "spring.redis")
@Data
public class RedissonConfig {

    private Integer database;

    private String host;

    private Integer port;

    private String password;

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://" + host + ":" + port)
                .setPassword(password)       // 👈 添加这一行用于设置 Redis 密码
                .setDatabase(database);
        RedissonClient redisson = Redisson.create(config);
        return redisson;
    }
}
编写 RedisLimiterManager:
// 表明这是一个 Spring 框架中的服务类,Spring 会自动将其实例化并管理
@Service
public class RedisLimiterManager {

    // 通过 Spring 的依赖注入机制,将 RedissonClient 实例注入到当前类中
    // RedissonClient 是 Redisson 框架的核心客户端,用于与 Redis 服务器进行交互
    @Resource
    private RedissonClient redissonClient;

    /**
     * 限流操作
     * 
     * @param key 用于区分不同的限流器,例如不同的用户 id 应分别进行统计
     *            每个不同的 key 会对应一个独立的限流器,可实现针对不同业务场景或用户的限流
     * @throws BusinessException 当请求超过限流阈值时,抛出业务异常,异常码为 ErrorCode.TOO_MANY_REQUEST
     */
    public void doRateLimit(String key) {
        // 从 RedissonClient 中获取一个名为 key 的限流器实例
        // RRateLimiter 是 Redisson 提供的用于实现限流功能的接口
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
        // 尝试为限流器设置速率规则
        // RateType.OVERALL 表示对整个系统进行限流,即不区分不同的客户端或节点
        // 2 表示每秒允许的最大操作次数
        // 1 表示时间间隔的数量
        // RateIntervalUnit.SECONDS 表示时间间隔的单位为秒
        // 此设置意味着每秒最多允许 2 次操作
        rateLimiter.trySetRate(RateType.OVERALL, 2, 1, RateIntervalUnit.SECONDS);
        // 每当有一个操作到来时,尝试从限流器中获取一个令牌
        // 若成功获取到令牌,说明该操作在限流范围内,可以继续执行
        // 若未获取到令牌,说明该操作超过了限流阈值
        boolean canOp = rateLimiter.tryAcquire(1);
        // 若未获取到令牌
        if (!canOp) {
            // 抛出业务异常,提示请求次数过多
            // ErrorCode.TOO_MANY_REQUEST 是自定义的错误码,用于表示请求过于频繁
            throw new BusinessException(ErrorCode.TOO_MANY_REQUEST);
        }
    }
}
单元测试:
// 表明这是一个 Spring Boot 的测试类,Spring Boot 会自动加载测试所需的上下文环境
@SpringBootTest
class RedisLimiterManagerTest {
    // 通过 Spring 的依赖注入机制,将 RedisLimiterManager 实例注入到当前测试类中
    // 这样就可以在测试方法中使用 RedisLimiterManager 的功能
    @Resource
    private RedisLimiterManager redisLimiterManager;

    /**
     * 测试 RedisLimiterManager 的 doRateLimit 方法
     * 
     * @throws InterruptedException 当线程在休眠期间被中断时抛出此异常
     */
    @Test
    void doRateLimit() throws InterruptedException {
        // 定义一个用户 ID,用于作为限流器的 key,模拟不同用户的限流情况
        String userId = "1";

        // 模拟在 1 秒内进行 2 次操作
        for (int i = 0; i < 2; i++) {
            // 调用 RedisLimiterManager 的 doRateLimit 方法进行限流操作
            redisLimiterManager.doRateLimit(userId);
            // 若操作未被限流,打印“成功”信息
            System.out.println("成功");
        }

        // 让当前线程休眠 1 秒,模拟时间间隔
        // 因为之前设置的限流器规则是每秒最多 2 次操作,休眠 1 秒后可以重新开始新的一轮计数
        Thread.sleep(1000);

        // 模拟在新的一秒内进行 5 次操作
        for (int i = 0; i < 5; i++) {
            // 调用 RedisLimiterManager 的 doRateLimit 方法进行限流操作
            redisLimiterManager.doRateLimit(userId);
            // 若操作未被限流,打印“成功”信息
            // 由于限流器规则是每秒最多 2 次操作,后续的操作可能会触发限流异常
            System.out.println("成功");
        }
    }
}

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐