限流
2025/10/18大约 2 分钟
限流
使用场景
| 场景 | 说明 | 示例 |
|---|---|---|
| 接口防刷 | 限制单个用户或IP访问频率 | 登录接口、短信验证码接口 |
| 秒杀系统 | 控制并发请求数,保护库存与数据库 | “双11”秒杀抢购接口 |
| 网关层限流 | 控制整体流量、防止突发流量打垮后端 | Nginx、Spring Cloud Gateway、Zuul |
| 服务间调用限流 | 控制下游依赖的访问频率 | 微服务调用:A → B,每秒最多 100 次 |
| MQ消费者限速 | 控制消费速率,避免下游处理不过来 | 消费 Kafka/RabbitMQ 数据 |
| 分布式系统全局限流 | 统一限制整个系统的请求速率 | Redis + Lua 实现全局限流 |
漏桶算法
请求先进入桶中;
桶以固定速率漏水(处理请求);
当桶满时,新请求直接丢弃。
请求流入
↓↓↓↓↓↓↓↓↓↓↓
┌────────────┐
│ 漏桶 │ ——→ 按固定速率流出
│ (容量N) │
└────────────┘令牌桶算法
令牌桶中定期生成令牌:
- 每个请求必须拿到令牌才能执行;
- 桶最多能装
capacity个令牌; - 没有令牌的请求要么等待,要么丢弃。
Redisson的RedissonRateLimiter
Redisson 的限流器实现了一个 令牌桶算法 (Token Bucket):
令牌按速率生成 → 桶容量上限 → 请求消耗令牌特性:
每秒生成固定数量令牌;
当桶满时,新令牌丢弃;
请求只有拿到令牌才能执行;
没有令牌 → 立即拒绝 或 等待一段时间。
使用
引入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.27.2</version> <!-- 使用最新版 -->
</dependency>配置 Redis(application.yml)
spring:
redis:
host: localhost
port: 6379
password: 123456
redisson:
config: |
singleServerConfig:
address: "redis://127.0.0.1:6379"
password: 123456注入 RedissonClient
import org.redisson.api.RedissonClient;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateType;
import org.redisson.api.RateIntervalUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class RedisRateLimiter {
@Autowired
private RedissonClient redissonClient;
public boolean tryAcquire(String key) {
RRateLimiter rateLimiter = redissonClient.getRateLimiter("limit:" + key);
// 初始化(只在首次执行时生效)
rateLimiter.trySetRate(
RateType.OVERALL, // 全局限流(也可选择 PER_CLIENT)
5, // 每秒最多5个请求
1, // 时间间隔1秒
RateIntervalUnit.SECONDS
);
return rateLimiter.tryAcquire(1); // 获取1个令牌
}
}实现限流接口
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
String key();
long permitsPerSecond();
}AOP实现
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class RateLimitAspect {
@Autowired
private RedisRateLimiter limiter;
@Around("@annotation(rateLimit)")
public Object around(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
String key = rateLimit.key();
long rate = rateLimit.permitsPerSecond();
if (!limiter.tryAcquire(key)) {
return "请求太频繁,请稍后再试!";
}
return joinPoint.proceed();
}
}使用
@GetMapping("/create")
@RateLimit(key = "createOrder", permitsPerSecond = 5)
public String createOrder() {
return "下单成功!";
}