看了一遍 SpringCloud gateway RequestRateLimiter 源码,本文来串联分析下它是如何用自动化部署方式提供默认的 RedisRateLimiter 支持的。
本文基于 3.1.0-SNAPSHOT 版本的源码进行分析。
自动注入拦截器工厂GatewayAutoConfiguration 类中关于限流拦截器实例注入的代码如下:
@Bean(name = PrincipalNameKeyResolver.BEAN_NAME)
@ConditionalOnBean(RateLimiter.class)
@ConditionalOnMissingBean(KeyResolver.class)
@ConditionalOnEnabledFilter(RequestRateLimiterGatewayFilterFactory.class)
public PrincipalNameKeyResolver principalNameKeyResolver() {
return new PrincipalNameKeyResolver();
}
@Bean
@ConditionalOnBean({ RateLimiter.class, KeyResolver.class })
@ConditionalOnEnabledFilter
public RequestRateLimiterGatewayFilterFactory requestRateLimiterGatewayFilterFactory(RateLimiter rateLimiter,
KeyResolver resolver) {
return new RequestRateLimiterGatewayFilterFactory(rateLimiter, resolver);
}
它需要容器中存在两种类的实例 { RateLimiter.class, KeyResolver.class },这个条件默认是成立的,因为 GatewayRedisAutoConfiguration 这个类默认为它注入了 RateLimiter 的实现类RedisRateLimiter 实例:
@Bean @ConditionalOnMissingBean public RedisRateLimiter redisRateLimiter(ReactiveStringRedisTemplate redisTemplate, @Qualifier(RedisRateLimiter.REDIS_script_NAME) Redisscript> redisscript, ConfigurationService configurationService) { return new RedisRateLimiter(redisTemplate, redisscript, configurationService); }
这样的话,就创建了限流拦截器了。
限流拦截器这两个属性,它们主要用于提供默认的限流算法和请求唯一性 Key 生成这两项信息的。如果程序中想要改变它们的实现类,可以修改拦截器的配置,指定这两个实例引用的 Bean 的名称,这个拦截器的配置类是这样定义的:
public static class Config implements HasRouteId {
private KeyResolver keyResolver;
private RateLimiter rateLimiter;
private HttpStatus statusCode = HttpStatus.TOO_MANY_REQUESTS;
private Boolean denyEmptyKey;
private String emptyKeyStatus;
private String routeId;
}
属性装配
C routeConfig = newConfig();
if (this.configurationService != null) {
this.configurationService.with(routeConfig).name(this.configurationPropertyName).normalizedProperties(args).bind();
}
配置文件中的:
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: https://example.org
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
args 参数是怎么被使用的呢?
name 指定的 FilterFactory 类全局有几个,针对每个 Route 的不同配置又是怎么处理的呢?
针对 RedisRateLimiter 的实例的参数配置是怎么在调用 isAllowed 时绑定到特定 Route 上的,这个过程还没有整理明白:
public MonoisAllowed(String routeId, String id) { if (!this.initialized.get()) { throw new IllegalStateException("RedisRateLimiter is not initialized"); } Config routeConfig = loadConfiguration(routeId); // How many requests per second do you want a user to be allowed to do? int replenishRate = routeConfig.getReplenishRate(); // How much bursting do you want to allow? int burstCapacity = routeConfig.getBurstCapacity(); // How many tokens are requested per request? int requestedTokens = routeConfig.getRequestedTokens(); 。。。后面源码略 }
从这段代码来看,每次调用限流方法 isAllowed 时,读取到的 RouteConfig 中配置都是与该路由有关的。
启示录因配置可以指定不同 routeId 都指向同一个拦截器工厂,而拦截器工厂实例是单例的,那么各个路由对应的拦截器实例的创建必定是依赖于对应的 args 配置,这样的话 apply(Config) 创建的拦截器实例就是各路由特有的了。



