您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# Spring Cloud Gateway中一段脚本如何实现令牌桶
## 引言
在现代分布式系统中,API限流是保障系统稳定性的重要手段之一。Spring Cloud Gateway作为Spring Cloud生态中的API网关组件,内置了基于Redis的分布式限流功能,其中令牌桶算法(Token Bucket)是最常用的实现方案之一。本文将深入剖析Spring Cloud Gateway中一段关键脚本如何实现令牌桶算法,并通过源码解析、算法原理和实际案例展示其实现细节。
---
## 一、令牌桶算法基础原理
### 1.1 算法核心思想
令牌桶算法通过以下机制工作:
- **固定速率生成**:系统以恒定速率(如每秒10个)向桶中添加令牌
- **容量限制**:桶有最大容量(如100个),超过时新令牌被丢弃
- **请求消耗**:每个请求需要获取1个令牌,无令牌时请求被限流
### 1.2 与漏桶算法对比
| 特性 | 令牌桶 | 漏桶 |
|---------------|---------------------------|--------------------------|
| 流量突发 | 允许(消耗积攒令牌) | 严格平滑 |
| 速率控制 | 平均速率+突发控制 | 严格固定速率 |
| 实现复杂度 | 中等 | 简单 |
---
## 二、Spring Cloud Gateway的Redis限流实现
### 2.1 核心依赖
```xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
spring:
cloud:
gateway:
routes:
- id: rate_limit_route
uri: http://example.org
predicates:
- Path=/api/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@userKeyResolver}"
Spring Cloud Gateway使用以下Lua脚本实现令牌桶算法:
local tokens_key = KEYS[1] -- 令牌桶剩余令牌数key
local timestamp_key = KEYS[2] -- 最后刷新时间key
local rate = tonumber(ARGV[1]) -- 令牌生成速率(每秒)
local capacity = tonumber(ARGV[2]) -- 桶容量
local now = tonumber(ARGV[3]) -- 当前时间戳(秒)
local requested = tonumber(ARGV[4])-- 请求令牌数(通常为1)
local fill_time = capacity / rate -- 填满桶所需时间
local ttl = math.floor(fill_time * 2) -- Redis key过期时间
local last_tokens = tonumber(redis.call("get", tokens_key))
if last_tokens == nil then -- 首次初始化
last_tokens = capacity
end
local last_refreshed = tonumber(redis.call("get", timestamp_key))
if last_refreshed == nil then
last_refreshed = 0
end
local delta = math.max(0, now - last_refreshed) -- 距离上次刷新的时间差
local filled_tokens = math.min(capacity, last_tokens + (delta * rate)) -- 计算新令牌数
local allowed = filled_tokens >= requested
local new_tokens = filled_tokens
local allowed_num = 0
if allowed then
new_tokens = filled_tokens - requested
allowed_num = 1
end
redis.call("setex", tokens_key, ttl, new_tokens) -- 更新剩余令牌数
redis.call("setex", timestamp_key, ttl, now) -- 更新刷新时间
return { allowed_num, new_tokens } -- 返回是否允许及剩余令牌数
local delta = math.max(0, now - last_refreshed)
local filled_tokens = math.min(capacity, last_tokens + (delta * rate))
delta
:计算自上次刷新后的时间差(秒)filled_tokens
:确保不超过桶容量的新令牌数Redis执行Lua脚本的原子性特性确保: 1. 计算和更新操作不可分割 2. 多客户端并发时不会出现竞态条件
local ttl = math.floor(fill_time * 2)
设置足够长的TTL防止key过早过期,同时避免Redis内存浪费
tonumber()
显式类型转换@Bean
public RedisScript<List<Long>> redisRequestRateLimiterScript() {
ScriptSource scriptSource = new ResourceScriptSource(
new ClassPathResource("META-INF/scripts/request_rate_limiter.lua"));
return RedisScript.of(scriptSource.getScriptAsString(), List.class);
}
通过Spring Bean方式注入脚本,支持Redis集群模式
@Bean
KeyResolver userKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getHeaders().getFirst("X-User-Id"));
}
@ExceptionHandler(ResponseStatusException.class)
public ResponseEntity<String> handleRateLimit(ResponseStatusException ex) {
if (ex.getStatusCode() == HttpStatus.TOO_MANY_REQUESTS) {
return ResponseEntity.status(429)
.header("X-RateLimit-Retry-After", "60")
.body("Rate limit exceeded");
}
return ResponseEntity.internalServerError().build();
}
参数 | 值 |
---|---|
线程数 | 50 |
持续时间 | 60秒 |
Ramp-up时间 | 5秒 |
RateLimiter limiter = RateLimiter.create(10.0); // 每秒10个令牌
if (limiter.tryAcquire()) {
// 处理请求
} else {
// 限流处理
}
区别: - 单机 vs 分布式 - 无Redis依赖 vs 需要Redis
FlowRule rule = new FlowRule()
.setResource("myApi")
.setGrade(RuleConstant.FLOW_GRADE_QPS)
.setCount(10);
FlowRuleManager.loadRules(Collections.singletonList(rule));
优势对比: - 更丰富的熔断降级规则 - 实时监控仪表盘
gateway.requests
和rate.limited
指标本文详细分析了Spring Cloud Gateway中基于Redis+Lua的令牌桶实现,揭示了: 1. 通过原子性脚本保证分布式环境下的准确性 2. 精妙的令牌计算和过期时间策略 3. 与Spring生态的无缝集成方式
这种实现方式在保证高性能的同时,提供了可靠的分布式限流能力,是微服务架构中流量控制的优选方案。
参见官方仓库 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。