摘要:本文主要介绍SpringBoot
项目的接口限流方案,市面上常用有2
种限流方案漏桶和令牌桶,大家常用的都是令牌桶,本文也只实现了令牌桶的方案。
漏桶和令牌桶说明
漏桶和令牌桶是最常用的两种限流算法,核心差异如下:
特性 | 漏桶限流(Leaky Bucket) | 令牌桶限流(Token Bucket) |
---|---|---|
核心逻辑 | 固定速率处理请求("漏水"),桶缓冲突发请求 | 固定速率生成令牌,请求需获取令牌才能处理 |
输出速率 | 严格固定(等于漏水速率) | 允许突发(最大为桶容量 + 新生成的令牌数) |
突发容忍度 | 不允许突发(超过桶容量的请求直接拒绝) | 允许突发(桶中令牌足够时,可一次性处理多个请求) |
资源利用率 | 可能较低(即使系统空闲,也需按固定速率处理) | 较高(系统空闲时可积累令牌,突发时快速处理) |
典型场景 | 严格要求速率稳定(如网络流量整形) | 允许一定突发(如 API 接口限流) |
项目基础配置
pom.xml
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.48.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.4.8-jre</version>
</dependency>
</dependencies>
application.yml
yaml
spring:
data:
redis:
host: redis
port: 6379
启动类
typescript
@SpringBootApplication
public class RateLimitApplication {
public static void main(String[] args) {
SpringApplication.run(RateLimitApplication.class, args);
}
}
基于Redisson
的令牌桶
在分布式系统中,使用 Redisson 实现并发限流是一种常见且高效的方式
RateType
限流键(key
)的设计
-
OVERALL
模式 :限流键需全局唯一(如global_api_limit
),确保所有请求共享同一个桶。 -
PER_CLIENT
模式 :限流键需能唯一标识客户端(如user:${userId}
、ip:${clientIp}
),确保每个客户端独立使用自己的桶。
less
@Slf4j
@RestController
@RequestMapping(value = "redisson/rate-limit")
public class RedissonRateLimitController {
@Autowired
private RedissonClient redissonClient;
@PostMapping("create-order")
public Object createOrder(String info){
RRateLimiter rateLimiter = getLimiter();
boolean b = rateLimiter.tryAcquire();
if(b) {
log.info("允许访问");
return "SUCCESS";
}
log.warn("限流了");
throw new RuntimeException("请求太火爆");
}
/**
* 获取限流器
* @return
*/
private RRateLimiter getLimiter() {
RRateLimiter rateLimiter = redissonClient.getRateLimiter("create-order");
rateLimiter.trySetRate(RateType.PER_CLIENT, 20, Duration.ofSeconds(5L));
return rateLimiter;
}
}
测试结果
我单线程请求了
8S
,请求成功40
个

Guava
的实现
Guava 的
RateLimiter
是单机环境下基于令牌桶算法的轻量级限流工具,适用于控制接口请求频率、资源访问速率或任务执行频率等场景。以下是不同场景下的使用案例,包含基础用法、高级特性及实际业务集成。
接口说明
RateLimiter.create(10.0)
:创建一个每秒生成 10 个令牌的限流器(即允许每秒 10 个请求)。RateLimiter rateLimiter = RateLimiter.create(20.0, 5, TimeUnit.SECONDS)
: //创建预热限流器:目标速率 20 个/秒,预热时间 5 秒(从 2 个/秒逐步提升到 20 个/秒)rateLimiter.acquire(1)
:阻塞等待获取 1 个令牌(若当前无令牌,会等待直到新令牌生成)。返回值为等待时间(秒)rateLimiter.tryAcquire(long permits, long timeout, TimeUnit unit)
:非阻塞获取
具体代码
less
@Slf4j
@RestController
@RequestMapping(value = "guava/rate-limit")
public class GuavaRateLimitController {
private static final RateLimiter rateLimiter = RateLimiter.create(5);
@PostMapping("create-order")
public Object createOrder(String info){
boolean b = rateLimiter.tryAcquire();
if(b) {
log.info("允许访问");
return "SUCCESS";
}
log.warn("限流了");
throw new RuntimeException("请求太火爆");
}
}
结果
8s
内成功访问45个请求

基于nginx
的限流
Nginx 限流核心模块:ngx_http_limit_req_module
Nginx 内置的
ngx_http_limit_req_module
基于漏桶算法实现限流,通过以下两个核心指令控制:
-
limit_req_zone
:定义限流的共享内存区和基础速率。 -
limit_req
:在具体location
或server
块中应用限流规则。
(1)定义限流共享内存区(limit_req_zone
)
在 Nginx 主配置文件(nginx.conf
)的 http
块中,定义共享内存区存储限流状态:
ini
http {
# 定义限流共享内存区(名称=one,大小=10MB,速率=10请求/秒)
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
# 其他全局配置...
include mime.types;
default_type application/octet-stream;
}
-
$binary_remote_addr
:限流的键(基于客户端 IP 地址)。 -
zone=one:10m
:共享内存区名称为one
,大小为 10MB(根据客户端数量调整,10MB 约支持 16 万 IP)。 -
rate=10r/s
:基础速率(每秒允许 10 个请求)。
(2)在 server
或 location
中应用限流规则
在需要限流的站点配置(如
server
块)中,使用limit_req
指令应用规则,并设置被限流时的响应:
ini
server {
listen 80;
server_name example.com;
location /api/ {
# 应用限流规则(使用上面定义的 zone=one)
limit_req zone=one burst=20 nodelay; # burst=突发容量,nodelay=不等待直接拒绝
# 被限流时返回 429 状态码,并跳转到自定义错误页
limit_req_status_code 429;
error_page 429 /custom_limit_msg;
# 正常请求的反向代理(示例)
proxy_pass http://backend_server;
}
# 自定义限流消息体(返回 JSON)
location = /custom_limit_msg {
internal; # 仅内部访问,禁止外部直接请求
add_header Content-Type application/json;
return 429 '{"code": 429, "message": "请求过于频繁,请稍后再试"}';
}
}
关键参数说明
参数/指令 | 含义 |
---|---|
limit_req_zone |
定义限流的共享内存区、键(如 IP)和基础速率(rate=10r/s 表示每秒 10 个请求)。 |
burst |
突发容量(允许短时间内超过基础速率的请求数),例如 burst=20 表示最多允许 20 个突发请求。 |
nodelay |
若启用,被限流的请求直接拒绝(不等待);若禁用(默认),请求会等待直到令牌可用。 |
limit_req_status_code |
设置被限流时的 HTTP 状态码(如 429 Too Many Requests )。 |
error_page |
自定义限流时的响应内容(如返回 JSON 消息体)。 |
高级限流场景
1. 按用户 ID 限流
若需按用户 ID 限流(如防止恶意用户高频请求),可将限流键改为用户 ID(需前端传递 X-User-ID
头):
ini
nginx
复制
http {
# 限流键为 X-User-ID 头的值(需确保头存在)
limit_req_zone $http_x_user_id zone=user_limit:10m rate=5r/s;
server {
location /api/user/ {
# 校验 X-User-ID 头是否存在,不存在则拒绝
if ($http_x_user_id = "") {
return 400 "Missing X-User-ID header";
}
# 应用限流(键为用户 ID)
limit_req zone=user_limit burst=10 nodelay;
limit_req_status_code 429;
error_page 429 /custom_limit_msg;
proxy_pass http://backend_server;
}
}
}