Guava 提供了一个轻量级的限流工具类 `RateLimiter`,适用于单机环境下的限流需求

本文探讨了如何在SpringBoot应用中使用LIMIT子句来限制SELECT查询返回的记录数。LIMIT接受一个或两个参数,用于设定返回记录的起始位置和最大数量。在MySQL中,也支持OFFSET语法以兼容PostgreSQL。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring Boot 实现限流功能的代码示例与配置管理

1. Guava RateLimiter 的简单实现

Guava 提供了一个轻量级的限流工具类 RateLimiter,适用于单机环境下的限流需求。以下是基于 Guava 的限流代码示例:
springboot系类代码:spring-boot-limit

import com.google.common.util.concurrent.RateLimiter;

@RestController
public class LimiterController {
   
   

    private static final double QPS = 5; // 每秒最多允许请求次数
    private final RateLimiter rateLimiter = RateLimiter.create(QPS);

    @GetMapping("/limited-resource")
    public String limitedResource() {
   
   
        if (!rateLimiter.tryAcquire()) {
   
    // 尝试获取令牌
            throw new RuntimeException("Too many requests, please try again later."); 
        }
        return "Access granted!";
    }
}

此方法适合小型项目或单体应用中的限流场景。


2. Redis + Lua 脚本实现分布式限流

对于分布式系统而言,使用 Redis 结合 Lua 脚本可以有效保证限流的一致性。以下是一个完整的实现案例:

(a) 初始化项目依赖

在项目的 pom.xml 中引入必要的依赖项:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
(b) 创建限流注解

定义一个自定义注解用于标记需要限流的方法:

@Target({
   
   ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
   
   
    int maxRequests(); // 最大请求数
    long timeWindowInSeconds(); // 时间窗口长度(单位:秒)
}
© 编写 Lua 脚本

创建一个 Lua 脚本来执行滑动窗口算法:

local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])

-- 获取当前时间戳
local now = redis.call('time')
now = tonumber(now[1]) * 1000 + math.floor(now[2] / 1000)

-- 删除过期记录
redis.call('zremrangebyscore', key, '-inf', tostring(now - window))

-- 添加新条目
redis.call('zadd', key, now, now)

-- 计算总数并清理多余数据
local count = tonumber(redis.call('zcard', key))
if count > limit then
    return 0 -- 超出限制
else
    redis.call('expire', key, window) -- 设置键的有效期
    return 1 -- 请求成功
end
(d) 构建 Redis 处理器

封装 Redis 操作逻辑以便于调用:

@Component
public class RedisRateLimiter {
   
   

    @Autowired
    private StringRedisTemplate redisTemplate;

    public boolean isAllowed(String key, int maxRequests, long timeWindowInSeconds) {
   
   
        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        script.setScriptSource(new ResourceScriptSource(
                new ClassPathResource("scripts/ratelimit.lua")));
        script.setResultType(Long.class);

        List<String> keys = Collections.singletonList(key);
        Long result = redisTemplate.execute(script, keys,
                String.valueOf(maxRequests), String.valueOf(timeWindowInSeconds));

        return result != null && result == 1;
    }
}
(e) 利用 AOP 实现拦截

编写切面类来拦截带有 @RateLimit 注解的方法:

@Aspect
@Component
public class RateLimitAspect {
   
   

    @Autowired
    private RedisRateLimiter redisRateLimiter;

    @Around("@annotation(rateLimit)")
    public Object handleRateLimitedMethod(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
   
   
        String key = generateKey(joinPoint); // 根据方法签名生成唯一 Key
        if (!redisRateLimiter.isAllowed(key, rateLimit.maxRequests(), rateLimit.timeWindowInSeconds())) {
   
   
            throw new RuntimeException(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bol5261

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值