用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 {
    // 被限流后的操作
}
相关推荐
tan180°1 小时前
MySQL表的操作(3)
linux·数据库·c++·vscode·后端·mysql
优创学社22 小时前
基于springboot的社区生鲜团购系统
java·spring boot·后端
why技术2 小时前
Stack Overflow,轰然倒下!
前端·人工智能·后端
幽络源小助理2 小时前
SpringBoot基于Mysql的商业辅助决策系统设计与实现
java·vue.js·spring boot·后端·mysql·spring
ai小鬼头3 小时前
AIStarter如何助力用户与创作者?Stable Diffusion一键管理教程!
后端·架构·github
简佐义的博客3 小时前
破解非模式物种GO/KEGG注释难题
开发语言·数据库·后端·oracle·golang
Code blocks4 小时前
使用Jenkins完成springboot项目快速更新
java·运维·spring boot·后端·jenkins
追逐时光者4 小时前
一款开源免费、通用的 WPF 主题控件包
后端·.net