美文网首页
spring cloud gateway 限流 RequestR

spring cloud gateway 限流 RequestR

作者: virtual灬zzZ | 来源:发表于2021-11-03 19:13 被阅读0次

查看RequestRateLimiterGatewayFilterFactory类,其中有2个重要的属性defaultRateLimiterdefaultRateLimiter,它们都是接口类型:

@ConfigurationProperties("spring.cloud.gateway.filter.request-rate-limiter")
public class RequestRateLimiterGatewayFilterFactory extends
        AbstractGatewayFilterFactory<RequestRateLimiterGatewayFilterFactory.Config> {

    /**
     * Key-Resolver key.
     */
    public static final String KEY_RESOLVER_KEY = "keyResolver";

    private static final String EMPTY_KEY = "____EMPTY_KEY__";

    private final RateLimiter defaultRateLimiter;

    private final KeyResolver defaultKeyResolver;

    /**
     * Switch to deny requests if the Key Resolver returns an empty key, defaults to true.
     */
    private boolean denyEmptyKey = true;

    /** HttpStatus to return when denyEmptyKey is true, defaults to FORBIDDEN. */
    private String emptyKeyStatusCode = HttpStatus.FORBIDDEN.name();

    public RequestRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter,
            KeyResolver defaultKeyResolver) {
        super(Config.class);
        this.defaultRateLimiter = defaultRateLimiter;
        this.defaultKeyResolver = defaultKeyResolver;
    }

...略

点进去 看它们的实现类:

  • keyResolver :它的意思是,限流的时候以什么维度来判断。 它有个实现类,PrincipalNameKeyResolver,它在没有配置的时候他就是默认类,以用户名来作为维度,它会从 ServerWebExchange检索 Principal并调用 Principal.getName()。因为Principal.getName()是空的,而denyEmptyKey =true,所以用它没有Principal的话,会返回forbidden403,就算设置denyEmptyKey =false,因为name是空,所以限流KeyResolver的键是空,限流会不生效。何况,我们一般会自己实现一个来取代。

  • rateLimiter:它的implement只有一个抽象类和redis的,redis是spring已经构建好了的,使用redis+lua脚本,采用的是令牌桶的限流方式,我们只需要按官网按需配置即可。

引入redis,顺带application文件配置redis。

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
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 #桶大小
            redis-rate-limiter.requestedTokens: 1 #默认是1,每次请求消耗的令牌数
  redis:
    port: 6379
    host: localhost #使用ip则把redis conf的 bind 127.0.0.1注释掉
    timeout: 10S        # 连接超时时间
    lettuce:
      pool:
        #连接池最大连接数(使用负值表示没有限制)
        max-active: 300
        #连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1S
        #连接池中的最大空闲连接
        max-idle: 100
        #连接池中的最小空闲连接
        min-idle: 20

GatewayAutoConfiguration:


    @Bean(name = PrincipalNameKeyResolver.BEAN_NAME)
    @ConditionalOnBean(RateLimiter.class)
    @ConditionalOnMissingBean(KeyResolver.class)
    public PrincipalNameKeyResolver principalNameKeyResolver() {
        return new PrincipalNameKeyResolver();
    }

    @Bean
    @ConditionalOnBean({ RateLimiter.class, KeyResolver.class })
    public RequestRateLimiterGatewayFilterFactory requestRateLimiterGatewayFilterFactory(
            RateLimiter rateLimiter, KeyResolver resolver) {
        return new RequestRateLimiterGatewayFilterFactory(rateLimiter, resolver);
    }

RedisRateLimiter:


@ConfigurationProperties("spring.cloud.gateway.redis-rate-limiter")
public class RedisRateLimiter extends AbstractRateLimiter<RedisRateLimiter.Config>
        implements ApplicationContextAware {

    /**
     * @deprecated use {@link Config#replenishRate}
     */
    @Deprecated
    public static final String REPLENISH_RATE_KEY = "replenishRate";

    /**
     * @deprecated use {@link Config#burstCapacity}
     */
    @Deprecated
    public static final String BURST_CAPACITY_KEY = "burstCapacity";

    /**
     * Redis Rate Limiter property name.
     */
    public static final String CONFIGURATION_PROPERTY_NAME = "redis-rate-limiter";

    /**
     * Redis Script name.
     */
    public static final String REDIS_SCRIPT_NAME = "redisRequestRateLimiterScript";

    /**
     * Remaining Rate Limit header name.
     */
    public static final String REMAINING_HEADER = "X-RateLimit-Remaining";

    /**
     * Replenish Rate Limit header name.
     */
    public static final String REPLENISH_RATE_HEADER = "X-RateLimit-Replenish-Rate";

    /**
     * Burst Capacity header name.
     */
    public static final String BURST_CAPACITY_HEADER = "X-RateLimit-Burst-Capacity";

    /**
     * Requested Tokens header name.
     */
    public static final String REQUESTED_TOKENS_HEADER = "X-RateLimit-Requested-Tokens";

...略

自行配置:

自定义KeyResolver,用uri作为限流维度:

@Component
public class UriKeyResolver implements KeyResolver {
    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        String requestPath = exchange.getRequest().getURI().getPath();
        return Mono.just(requestPath);
    }
}

配置

- name: RequestRateLimiter #不能简写,因为factory没有shortcut方法,key-resolver使用自己的bean
     args:
       key-resolver: '#{@uriKeyResolver}'
       #deny-empty-key: false
       redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充速率
       redis-rate-limiter.burstCapacity: 5 #令牌桶容量
       redis-rate-limiter.requestedTokens: 1 #每次消耗令牌个数

测试结果:


生成2个redis key,它们的ttl都很短

  • request_rate_limiter.{限流维度名字}.tokens
  • request_rate_limiter.{限流维度名字}.timestamp

没有拿到token的,会返回429状态码,too many requests。

额外:

由于gateway转发的那方A,会调用另一番B,A对B有设置hystrix的配置,但是测试过后,对于gateway来说是完全无关的,B对A有降级熔断,该返回什么还是返回什么,gateway仅仅是个中介转发。


参考:
spring-cloud-gateway 11 限流 RequestRateLimiterGatewayFilterFactory 源码讲解
Spring Cloud 系列之 Gateway 服务网关(四) 限流算法讲解

相关文章

网友评论

      本文标题:spring cloud gateway 限流 RequestR

      本文链接:https://www.haomeiwen.com/subject/wmhczltx.html