【Springboot starter 组件开发】限流组件 RateLimiter

【Springboot starter 组件开发】限流组件 RateLimiter

  • 一、摘要
  • 二、基于guava实现
    • [2.1 核心依赖](#2.1 核心依赖)
    • [2.2 核心逻辑](#2.2 核心逻辑)
  • [三、基于Redis + lua脚本实现](#三、基于Redis + lua脚本实现)
    • [3.1 核心依赖](#3.1 核心依赖)
    • [3.2 核心逻辑](#3.2 核心逻辑)

一、摘要

  1. 基于guava的RateLimiter,实现限流
  2. 基于redis + lua脚本(推荐,准确性高),实现限流
  3. 掌握springboot starter的开发流程
  4. 源码地址:ratelimiter-spring-boot-starter

二、基于guava实现

2.1 核心依赖

xml 复制代码
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>23.5-jre</version>
</dependency>

2.2 核心逻辑

java 复制代码
@Slf4j
public class GuavaLimiter implements LimiterManager {

    private final Map<String, RateLimiter> limiterMap = Maps.newConcurrentMap();

    @Override
    public boolean tryAccess(LimiterEntity entity) {
        if (StringUtils.isBlank(entity.getKey())) {
            throw new LimiterException("Guava limiter key cannot be empty");
        }
        RateLimiter rateLimiter = getRateLimiter(entity);
        if (rateLimiter == null) {
            return false;
        }
        boolean result = rateLimiter.tryAcquire(entity.getPermitsPerSecond(), entity.getTimeout(), TimeUnit.SECONDS);
        log.info("Guava limiter tryAccess, key={}, result={}", entity.getKey(), result);
        return result;
    }

    private RateLimiter getRateLimiter(LimiterEntity entity) {
        String key = entity.getKey();
        // 先看缓存中是否存在
        if (!limiterMap.containsKey(key)) {
            // 缓存中不存在,则创建令牌桶,预热时间设置为1s
            RateLimiter rateLimiter = RateLimiter.create(entity.getPermitsPerSecond(), 1, TimeUnit.SECONDS);
            limiterMap.put(key, rateLimiter);
            log.info("Guava limiter new bucket, key={}, permits={}", key, entity.getPermitsPerSecond());
            return rateLimiter;
        }
        return limiterMap.get(key);
    }
}

三、基于Redis + lua脚本实现

3.1 核心依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <scope>provided</scope>
</dependency>

3.2 核心逻辑

java 复制代码
@Slf4j
public class RedisLimiter implements LimiterManager {

    private final StringRedisTemplate stringRedisTemplate;

    public RedisLimiter(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean tryAccess(LimiterEntity entity) {

        if (StringUtils.isBlank(entity.getKey())) {
            throw new LimiterException("Redis limiter key cannot be empty");
        }
        List<String> keys = Collections.singletonList(entity.getKey());

        double permitsPerSecond = entity.getPermitsPerSecond();
        long timeout = entity.getTimeout();

        RedisScript<Long> redisScript = new DefaultRedisScript<>(buildLuaScript(), Long.class);

        Long count = stringRedisTemplate.execute(redisScript, keys, "" + permitsPerSecond, "" + timeout);

        log.info("Redis limiter tryAccess, key={}, count={} ", entity.getKey(), count);

        return count != null && count != 0;
    }

    private String buildLuaScript() {
        return "--获取KEY\n" +
                "local key = KEYS[1]\n" +
                "\n" +
                "local limit = tonumber(ARGV[1])\n" +
                "\n" +
                "local curentLimit = tonumber(redis.call('get', key) or \"0\")\n" +
                "\n" +
                "if curentLimit + 1 > limit\n" +
                "    then return 0\n" +
                "else\n" +
                "    -- 自增长 1\n" +
                "    redis.call('INCRBY', key, 1)\n" +
                "    -- 设置过期时间\n" +
                "    redis.call('EXPIRE', key, ARGV[2])\n" +
                "    return curentLimit + 1\n" +
                "end";
    }
}
相关推荐
小_太_阳21 分钟前
Scala_【1】概述
开发语言·后端·scala·intellij-idea
智慧老师30 分钟前
Spring基础分析13-Spring Security框架
java·后端·spring
lxyzcm31 分钟前
C++23新特性解析:[[assume]]属性
java·c++·spring boot·c++23
V+zmm101341 小时前
基于微信小程序的乡村政务服务系统springboot+论文源码调试讲解
java·微信小程序·小程序·毕业设计·ssm
Oneforlove_twoforjob1 小时前
【Java基础面试题025】什么是Java的Integer缓存池?
java·开发语言·缓存
xmh-sxh-13142 小时前
常用的缓存技术都有哪些
java
搬码后生仔2 小时前
asp.net core webapi项目中 在生产环境中 进不去swagger
chrome·后端·asp.net
迷糊的『迷』2 小时前
vue-axios+springboot实现文件流下载
vue.js·spring boot
凡人的AI工具箱2 小时前
每天40分玩转Django:Django国际化
数据库·人工智能·后端·python·django·sqlite
AiFlutter2 小时前
Flutter-底部分享弹窗(showModalBottomSheet)
java·前端·flutter