用Redisson的RRateLimiter做限流【2】——大QPS限流策略

一、背景

线上有一个接口需要限流10W的QPS,这个时候如果再使用Redisson的RRateLimiter做限流,就会造成热key问题,会压垮redis。

二、解决方案

方案一:通过请求参数取模打散创建多个的RRateLimiter

暂时不使用这种方案。

方案二:每次从Redis取多个令牌到本地机器

每次从Redis取多个令牌到本地机器,本地机器的令牌消耗完了再用RRateLimiter向Redis取令牌。

方案三:方案一+方案二的一个组合

2.1 引入maven依赖

xml 复制代码
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.16.4</version>
</dependency>

2.2 实现代码

2.2.1 本地限流器
java 复制代码
public class CustomRateLimiter {
    private final RRateLimiter rateLimiter;
    // 用原子变量来缓存令牌数量
    private final AtomicLong tokens;
    // 每次从 Redis 获取的令牌数量
    private final long batchSize;
    
    public CustomRateLimiter(RRateLimiter rRateLimiter, long batchSize) {
        this.rateLimiter = rRateLimiter;
        // 比如:10
        this.batchSize = batchSize;
        // 初始化令牌数量
        this.tokens = new AtomicLong(0);
    }
    
    public boolean tryAcquire() {
        // 尝试获取本地令牌
        long currentTokens = tokens.get();
        if (currentTokens > 0) {
            // 如果本地缓存的令牌足够,直接消耗一个,成功获取令牌
            return tokens.decrementAndGet() >= 0;
        } else {
            // 从 Redis 获取 batchSize 个令牌
            if (rateLimiter.tryAcquire(batchSize)){
                // 先消耗一个,再更新
                tokens.addAndGet(batchSize - 1);
                return true;
            }else {
                return false;
            }
        }
    }
}
2.2.2 用bean管理本地限流器
java 复制代码
@Service
public class RateLimiterConfiguration {
    @Autowired
    @Qualifier("RRateLimiter")
    private RRateLimiter myRRateLimiter;
    
    @Bean(name = "myCustomRateLimiter")
    public CustomRateLimiter getMyRateLimiter() {
        return new CustomRateLimiter(myRRateLimiter, 10);
    }
}
2.2.3 RRateLimiter配置项
java 复制代码
@Configuration
public class RedissonConfiguration {
    @Bean(name = "rateLimiterRedissonClient")
    public RedissonClient getRateLimiterRedissonClient() {
        Config config = new Config();
        String clusterNodes = "redis集群节点信息(127.0.0.1:6379)";
        int connectionMinIdleSize = 10; // 连接池最小空闲线程数
        int connectionMaxSize = 100; // 连接池最大连接数
        
        if (StringUtils.isBlank(clusterNodes)) {
            throw new RuntimeException("Redisson 初始化失败, 没有配置集群地址");
        }
        
        String[] nodes = clusterNodes.split(",");
        List<String> newNodes = new ArrayList<>(nodes.length);
        Arrays.stream(nodes)
                .forEach((index) -> newNodes.add(index.startsWith("redis://") ? index : "redis://" + index));

        config.useClusterServers()
                .addNodeAddress(newNodes.toArray(new String[0]))
                .setMasterConnectionMinimumIdleSize(connectionMinIdleSize)
                .setSlaveConnectionMinimumIdleSize(connectionMinIdleSize)
                .setMasterConnectionPoolSize(connectionMaxSize)
                .setSlaveConnectionPoolSize(connectionMaxSize);
        
        config.setTransportMode(TransportMode.NIO);
        return Redisson.create(config);
    }
}
java 复制代码
@Configuration
public class RRateLimiterConfiguration {
    
    @Autowired
    @Qualifier("rateLimiterRedissonClient")
    private RedissonClient rateLimiterRedissonClient;
    
    @Bean(name = "RRateLimiter")
    public RRateLimiter getUserTimeGetCoinRRateLimiter() {
        String RRateLimiterName = "限流器名字";
        RRateLimiter RRateLimiter = rateLimiterRedissonClient.getRateLimiter(RRateLimiterName);
        long RRateLimiterFre = 3; // 限流器令牌桶最大大小 也是N秒内发放的令牌桶数量
        long RRateLimiterTime = 5; // N 秒内限制通过令牌的数量的N(时间参数)
        RRateLimiter.trySetRate(RateType.OVERALL,
                RRateLimiterFre, RRateLimiterTime, RateIntervalUnit.SECONDS);
        return RRateLimiter;
    }
}
2.2.4 使用
java 复制代码
@Autowired
@Qualifier("myCustomRateLimiter")
private CustomRateLimiter myCustomRateLimiter;

if (myCustomRateLimiter.tryAcquire()) {
    // 执行被限流的业务逻辑
} else {
    // 被限流后的操作
}
相关推荐
笃行3502 小时前
SQLServer数据迁移之后,那张报表还能不能秒出
后端
LucianaiB2 小时前
别再被 AI 骗了:我把腾讯云 4 个 Skill 做成了个「AI 打假侦探」,过程的一个小配置坑惨了我
后端
candyTong3 小时前
Claude Code 如何恢复一段会话
后端·架构·ai编程
En^_^Joy4 小时前
Django项目配置全攻略:settings配置文件
后端·python·django
IT_陈寒6 小时前
Python线程池吞了异常还不告诉我,这谁顶得住啊
前端·人工智能·后端
小强库计算机毕业设计6 小时前
SpringBoot+Vue3 学生宿舍管理系统实战
java·spring boot·后端·vue·学生宿舍管理系统
小狼1836 小时前
实践6|SDD 实战:AI 写的规格被推翻了四次
后端·claude
篮框坏了7 小时前
"DeepSeek Harness 实测:一毛钱干三活,但默认配置是个坑"
后端·ai编程
jobBridge217 小时前
大模型到底是怎么"想"的?我把 Transformer 拆开,发现它其实是个"接词狂魔"
人工智能·后端·编程语言
唐青枫8 小时前
看懂内存地址之后,才算真正入门 Zig:指针、切片与实战
后端