SpringBoot实现接口限流的常用方案
目录
- 漏桶和令牌桶说明
- 项目基础配置
- pom.XML
- application.yml
- 启动类
- 基于Redisson的令牌桶
- RateType限流键(key)的设计
- 测试结果
- Guava的实现
- 接口说明
- 具体代码
- 结果
- 基于nginx的限流
- Nginx 限流核心模块:ngx_http_limit_req_module
- 高级限流场景
漏桶和令牌桶说明
漏桶和令牌桶是最常用的两种限流算法,核心差异如下:
特性 | 漏桶限流(Leaky Bucket) | 令牌桶限流(Token Bucket) |
---|---|---|
核心逻辑 | 固定速率处理请求(“漏水”),桶缓冲突发请求 | 固定速率生成令牌,请求需获取令牌才能处理 |
输出速率 | 严格固定(等于漏水速率) | 允许突发(最大为桶容量 + 新生成的令牌数) |
突发容忍度 | 不允许突发(超过桶容量的请求直接拒绝) | 允许突发(桶中令牌足够时,可一次性处理多个请求) |
资源利用率 | 可能较低(即使系统空闲,也需按固定速率处理) | 较高(系统空闲时可积累令牌,突发时快速处理) |
典型场景 | 严格要求速率稳定(如网络流量整形) | 允许一定突发(如 API 接口限流) |
项目基础配置
pom.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
spring: data: redis: host: redis port: 6379
启动类
@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}
),确保每个客户端独立使用自己的桶。
@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 个令牌www.devze.com(若当前无令牌,会等待直到新令牌生成)。返回值为等待时间(秒)ratwww.devze.comeLimiter.tryAcquire(long permits, long timeout, TimeUnit unit)
:非阻塞获取
具体代码
@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();tVgdpPXs 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
块中,定义共享内存区存储限流状态:
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指令应用规则,并设置被限流时的响应:
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 | 自js定义限流时的响应内容(如返回 JSON 消息体)。 |
高级限流场景
1. 按用户 ID 限流
若需按用户 ID 限流(如防止恶意用户高频请求),可将限流键改为用户 ID(需前端传递 X-User-ID
头):
http { # 限流键为 X-User-ID 头的值(需确保头存在) limit_req_zone $http_x_user_id zone=user_limit:10m rate=5r/s; server { javascript 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; } } }
以上就是SpringBoot实现接口限流的常用方案的详细内容,更多关于SpringBoot接口限流的资料请关注编程客栈(www.devze.com)其它相关文章!
精彩评论