这篇文章主要分享使用Redisson在Spring Cloud Gateway 中实现分布式自定义限流
为啥不使用Sentinel?
想到分布式限流组件,大家很容易想到Sentinel,但是其实有很多局限性,首先Sentinel的流量控制有两个维度的控制:并发线程数 和QPS,它们都是针对某个具体的接口来设置的
Sentinel的限流原理主要是通过统计系统的QPS(即每秒请求数量)和并发量来控制系统的流量,从而达到限流的目的。
其实之前分享过Sentinel限流的应用,大家感兴趣的话可以先看下了解下。
可以看出目前不支持自定义时间窗口,与分布式限流,以及自定义规则限流,比如 IP地址,用户等维度限流功能
常用限流方案比对
在 Spring Cloud Gateway 中实现分布式限流时,Redisson 方案 与原生 RequestRateLimiter 是两种主流方案,以下是核心对比与选型建议:
特性 | Redisson 方案 | RequestRateLimiter (原生) |
---|---|---|
分布式支持 | ✅ 基于 Redis 集群,天然分布式 | ✅ 基于 Redis,依赖 Lua 脚本实现分布式 |
时间窗口灵活性 | ✅ 秒/分/时/天级自由配置 (RateIntervalUnit ) |
❌ 仅支持秒级窗口(需扩展) |
限流算法 | ✅ 令牌桶算法(允许突发流量) | ✅ 令牌桶算法(固定窗口) |
动态规则更新 | ✅ 通过 trySetRate() 实时调整规则 |
❌ 需重启服务或手动刷新配置 |
限流维度 | ✅ 支持 IP、用户、API 等多维度组合键 | ✅ 需自定义 KeyResolver 实现 |
依赖复杂度 | 需引入 Redisson 客户端 | 仅需 Spring Data Redis Reactive |
Redisson 核心优势:
-
灵活时间窗口:支持分钟/小时级限流(如 1 分钟 100 次请求)
-
动态生效:限流规则可运行时热更新;
-
精细控制 :提供
RateType.PER_CLIENT
(按客户端限流)和RateType.OVERALL
(全局限流)
Redisson 分布式限流总体设计

处理流程
- 客户端请求到达 Spring Cloud Gateway。
- Gateway 根据配置的 KeyResolver 解析出限流的键(例如:IP、用户ID、API等)。
- 根据路由地址,从动态配置中获取限流规则(例如:每秒100次)。
- 使用 Redis 执行 Lua 脚本(保证原子性)进行限流计数和判断。
- 如果未超过阈值,请求放行;否则返回429(Too Many Requests)。
- 动态配置管理:通过管理后台更新限流规则,并同步到所有网关节点。

redission令牌桶限流算法

系统以固定速率(如每秒 100 个)向桶中添加令牌。例如:
java
rateLimiter.trySetRate(RateType.OVERALL, 100, 1, RateIntervalUnit.SECONDS);
rate
:令牌生成速率(100 个/秒)interval
+unit
:时间窗口(1 秒)
Redisson 分布式限流的具体代码实现
引入依赖版本
版本兼容性
这边在引入Redisson版本的,一定要注意与gateway版本兼容还有Redis服务版本问题,具体可以参考下,优先根据 Spring Boot 版本选择 Redisson,再匹配 Spring Cloud Gateway
- Spring Boot 2.4.x → Redisson 3.17.x
- Spring Boot 2.7.x → Redisson 3.23.x
- Spring Boot 3.2.x → Redisson 3.27.x+
场景 | Spring Cloud Gateway | Redisson | Spring Boot | Redis Server |
---|---|---|---|---|
传统项目(兼容旧版) | 2.2.2.RELEASE | 3.17.7 | 2.4.13 | 6.x |
新项目(推荐) | 4.2.0 | 3.27.1 | 3.2.4 | 7.x |
我这边与gateway版本兼容问题 版本是2.1.1.RELEASE,引入的redisson版本是3.13.6,大家可以做个参考。
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
xml
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.6</version>
</dependency>
初始化 RedissonClient
大家注意下这里 config.useClusterServers() 要根据自己的redis服务器选择不同的配置
java
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
@Value("${redis.hosts}")
private String redisHost;
@Value("${redis.ports}")
private int redisPort;
@Value("${redis.password}")
private String redisPassword;
@Value("${redis.timeout}")
private int timeout;
@Bean(destroyMethod ="shutdown")
public RedissonClient redissonClient() {
Config config = new Config();
config.useClusterServers()
.addNodeAddress("redis://" +redisHost+ ":" + redisPort)
.setPassword(redisPassword)
.setConnectTimeout(timeout);
return Redisson.create(config);
}
}
规则存储
规则默认存储在mysql数据库中,就是一个很简单的结构,可以根据自己需求扩展
sql
CREATE TABLE `limit_rules` (
`id` bigint(20) NOT NULL,
`request_user` varchar(64) DEFAULT NULL COMMENT '请求用户',
`request_ip` varchar(64) DEFAULT NULL COMMENT '请求IP',
`request_url` varchar(255) NOT NULL COMMENT '请求地址',
`limit_count` int(11) NOT NULL COMMENT '限制数量',
`window_size` int(11) NOT NULL COMMENT '时间窗口/秒',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uni_request_url` (`request_url`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='分布式限流规则表';
网关定时任务,每隔30s拉取最新的规则数据存取在内存中
java
@Service
public class RateLimitRuleService {
@Autowired
private RateLimitRuleRepository ruleRepository;
private final Map<String, RateLimitRule> ruleCache = new ConcurrentHashMap<>();
// 每30秒刷新规则缓存
@Scheduled(fixedRate = 30000)
public void refreshRules() {
ruleRepository.findAll().forEach(rule ->
ruleCache.put(rule.getRouteId() + ":" + rule.getKeyExpression(), rule)
);
}
}
自定义限流过滤器
这里有点需要注意,规则删除之后,一点要清除redis中附属的key的计数数据
java
@Component
public class RedissonRateLimitFilter implements GlobalFilter {
@Autowired
private RedissonClient redissonClient;
@Autowired
private RateLimitRuleService ruleService;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String key = resolveKey(exchange);
// 1. 查询动态规则
RateLimitRule rule = ruleService.getRule( key);
//这里规则逻辑就省略了,大家自己根据自己规则设置
if (rule == null) {
return chain.filter(exchange); // 无规则则放行
}
if(rule.isEnabled()){
// 如果规则被标记为删除,则直接返回
log.error("RedissonRateLimitFilter rule for path {} has been deleted", path);
this.destroyRateLimiter(key, path);
return chain.filter(exchange);
}
// 2. 初始化Redisson限流器
RRateLimiter rateLimiter = redissonClient.getRateLimiter("rate_limit:" + rule.getId() + ":" + key);
rateLimiter.trySetRateAsync(
RateType.OVERALL,
rule.getLimitCount(),
rule.getWindowSize(),
RateIntervalUnit.SECONDS
);
// 4. 尝试获取令牌
if (rateLimiter.tryAcquire()) {
return chain.filter(exchange); // 放行
} else {
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().setComplete(); // 返回429
}
}
public void destroyRateLimiter(String key,String requestUrl) {
RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
// 1. 删除主配置
rateLimiter.delete();
// 2. 删除附属 Key
this.deleteAuxiliaryKeys(key);
//3. 删除数据库中规则
ruleService.deleteRule(requestUrl);
}
private void deleteAuxiliaryKeys(String key) {
String valueKey = key + ":value";
String permitsKey = key + ":permits";
// 批量异步删除
RBatch batch = redissonClient.createBatch();
batch.getBucket(valueKey).deleteAsync();
batch.getScoredSortedSet(permitsKey).deleteAsync();
batch.execute();
}
}
以上就是相关的代码,可以看到使用redisson还是很简单的就可以实现分布式限流的功能
压测数据
最后我们来看下压测相关数据,看下对性能影响如何?
对接口进行对照压测,线程组配置如下
1.无任何配置,吞吐 113/s
2.对接口进行限流配置,但是配置远大于请求数据,吞吐113.3/s
3.对接口进行限流配置,但是配置远小于请求数据,大部分接口限流,吞吐115.3/s
因为压测接口没有任何逻辑,直接返回,所以看上去没有实际的差距,这里也懒得再重新压测了,感兴趣的可以自己再压测下相关业务接口,但是实际我们上线了生产环境一个月了,目前来看对于性能影响来看很小