Spring Cloud Gateway内置GatewayFilter工厂类 (二)

5.6 PrefixPath GatewayFilter Factory

  PrefixPath过滤器工厂类的实现类是PrefixPathGatewayFilterFactory,这个类只需要配置一个prefix参数,它可以给请求的URI添加prefix前缀。如下面配置所示:

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: http://example.org
        filters:
        - PrefixPath=/mypath

这样的话这个过滤器就会给所示匹配的请求URI添加一个前缀,比如上面的配置,如果收到的请求URI是/hello,那么经过此过滤器之后URI就变为/mypath/hello了。

5.7 PreserveHostHeader GatewayFilter Factory

    PreserveHostHeader过滤器工厂的实现类是PreserveHostHeaderGatewayFilterFactory,它表示在Spring Cloud Gateway转发请求的时候,保持客户端的Host信息不变,会将这些信息携带到后面的服务实例上面。默认为开启这个过滤器的。要不然后面的实例服务接收到的Host信息就是新的Http Client的Host信息,而不是真实的客户端的Host信息了。配置如下所示:

spring:
  cloud:
    gateway:
      routes:
      - id: preserve_host_route
        uri: http://example.org
        filters:
        - PreserveHostHeader

5.8 RequestRateLimiter GatewayFilter Factory

  RequestRateLimiter过滤器工厂的实现类是RequestRateLimiterGatewayFilterFactory  , 这是一个请求速率限制器,它使用RateLimiter的具体规则决定是否允许处理当前的请求。如果不允许,将返回一个默认的状态码   HTTP 429 - Too Many Requests  (请求太频繁)。

     另外,这个过滤器使用一个可选择的参数:keyResolver,这些具体的keyResolver决定了速率限制的规则。 keyResolver是一个实现了KeyResolver接口的Bean类,在配置中,可以使用SpEl表达式引用这个Bean的名字。例如#{@myKeyResolver}就是一个SpEL表达式,它引用了一个名字叫myKeyResolver的Bean。KeyResolver接口如下面代码所示:

public interface KeyResolver {
    Mono<String> resolve(ServerWebExchange exchange);
}

使用KeyResolver接口可以灵活的配置速率限制器的使用的key,这个key是指限流器限制的依据,可以从exchange中获取key的具体值,比如根据ip限流,根据某个userId限流等,在未来的计划中,将会添加更多的KeyResolver实现。默认的KeyResolver实现是PrincipalNameKeyResolver,它允许从ServerWebExchange中获取Principal,并调用Principal.getName();具体的用法可以参考下面Redis RateLimiter的使用.

一般来说,如果KeyResolver找不到一个key,本次请求将会被拒绝,这个行为可以通过配置进行调整:spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key(true 或false)和 spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code (拒绝请求时返回的状态码)

注意,RequestRateLimiter不支持快捷方式配置,如下面的配置是错误的:

# INVALID SHORTCUT CONFIGURATION
spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver}

 

5.8.1 Redis RateLimiter

  这是一个基于redis实现的限流器,Stripe公司(国外一家做支付的公司)目前正在使用这一方式。它需要用到一个Spring boot starter : spring-boot-starter-data-redis-reactive。

如下面配置所示:

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

此流量限制方式使用的是令牌桶算法(Token Bucket Algorithm)。这个限流器有两个重要的参数:

  • redis-rate-limiter.replenishRate:这个是令牌产生的速率,表示每秒钟产生多少个令牌放入到令牌桶中,在没有任务请求被丢弃的情况下,所有的令牌被使用,用户在一秒内正好发送这么多请求。这也是对请求的速率进行限制。
  • redis-rate-limiter.burstCapacity:这个表示的是令牌桶的容量,表示在这个令牌中最多有这么多令牌,也即表示,一个用户同时允许最多发送的请求数。这个是上面产生速率的一种限制,因为令牌是被以固定的速率放入到令牌桶中的,如果这一秒内放的令牌没有被使用,下一秒还会以此速度放入令牌,直到达到令牌桶的容量限制。

比如replenishRate配置的是2,burstCapacity配置是6,那么就表示一秒内,一个用户最多发送6个请求(令牌桶满的时候),之后如果请求不断,也是最多以2次请求/每秒的速率处理请求。

如果replenishRate和burstCapacity的值相关,这就会使网关以一个固定的速率处理请求,如果burstCapacity配置为0,则会阻塞所有的请求。burstCapacity的值设置的比replenishRate大一些,表示可以允许临时的大速率处理请求。

如下面配置的例子所示:

spring:
  cloud:
    gateway:
      routes:
      - id: requestratelimiter_route
        uri: http://example.org
        filters:
        - name: RequestRateLimiter
          args:
            redis-rate-limiter.replenishRate: 10
            redis-rate-limiter.burstCapacity: 20
@Bean
KeyResolver userKeyResolver() {
    return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));  //根据参数进行限流
}

这个限流器配置示表,限制每个用户每秒钟发送10次请求,最多20次,在某个时间内,允许的请求在可以在10到20之间。如果连续两次发送10次请求,就会导致请求被丢弃,返回Http状态码 HTTP 429 - Too Many Requests 。上面Java中配置的Bean类:KeyResolver,表示获取限流的key,它是从请求参数中获取的,生产环境不建议这样使用,因为如果每次请求这个值不一样,是起不到限流作用的。

继承RateLimiter 接口也可以实现自定义的限流器,在配置中,使用SpEL表达式来引用一个限流器的Bean。比如#{@myRateLimiter}就是一个SpEL表达式,表示引用一个bean,它的名字是myRateLimiter。如下面配置所示:

spring:
  cloud:
    gateway:
      routes:
      - id: requestratelimiter_route
        uri: http://example.org
        filters:
        - name: RequestRateLimiter
          args:
            rate-limiter: "#{@myRateLimiter}"
            key-resolver: "#{@userKeyResolver}"  

 可以参考源码项目中的配置方式:

 

 

对就的Bean配置如下所示:

@Configuration
public class RateLimiterConfig {

    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange-> Mono.just(exchange.getRequest().getRemoteAddress().getHostString());
    }
    @Bean
    public RedisRateLimiter myRateLimiter() {
        return new RedisRateLimiter(2, 4);
    }
}

源码地址:https://gitee.com/wgslucky/SpringCloud

5.9 RedirectTo GatewayFilter Factory

     这个跳转的过滤器的实现类是:RedirectToGatewayFilterFactory,它需要两个参数,一个是状态码,一个是重定向要跳转到的Url,这个Url必须是可访问的,它会被添加到此请求返回的消息中的Location Header里面,客户端会从Location Header中获取这个Url并发送请求。这个状态码必须是30x系列,要不然程序会报错,常用的状态码有301和302

  • 301:在请求的URL已被移除时使用。响应的Location首部中应该包含资源现在所处的URL。 
  • 302:与301状态码类似,但是,客户端应该使用Location首部给出的URL来零食定位资源,将来的请求仍然使用老的URL。

官方的比较简洁的说明:

  • 301 redirect: 301 代表永久性转移(Permanently Moved)
  • 302 redirect: 302 代表暂时性转移(Temporarily Moved )

尽量使用301跳转!301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。他们的不同在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。重定向是客户端行为。

    在application.yml中配置如下所示:

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: http://localhost:8080
        predicates:
        - Path=/redirect_test
        filters:
        - RedirectTo=301,http://www.xinyues.com

启动源码中的网关服务,请求http://localhost:8080/redirect_test ,将会跳转到http://www.xinyues.com

 

5.10 RemoveNonProxyHeaders GatewayFilter Factory

原文档如下所示:

The RemoveNonProxyHeaders GatewayFilter Factory removes headers from forwarded requests. The default list of headers that is removed comes from the IETF.
The default removed headers are:
Connection
Keep-Alive
Proxy-Authenticate
Proxy-Authorization
TE
Trailer
Transfer-Encoding
Upgrade
To change this, set the spring.cloud.gateway.filter.remove-non-proxy-headers.headers property to the list of header names to remove.

但是在spring-cloud-gateway-core-2.1.0.RELEASE.jar版本的jar并没有找到此过滤器工作,也没有spring.cloud.gateway.filter.remove-non-proxy-headers.headers的配置,但是找到spring.cloud.gateway.filter.remove-hop-by-hop.headers的配置。如果要指定移除的Header,可以在这里面配置。默认移除的Header有:

public static final Set<String> HEADERS_REMOVED_ON_REQUEST =
            new HashSet<>(Arrays.asList(
                    "connection",
                    "keep-alive",
                    "transfer-encoding",
                    "te",
                    "trailer",
                    "proxy-authorization",
                    "proxy-authenticate",
                    "x-application-context",
                    "upgrade"
                    // these two are not listed in https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3
                    //"proxy-connection",
                    // "content-length",
                    ));

5.11 RemoveRequestHeader GatewayFilter Factory

  这个过滤器的实现类是:RemoveRequestHeaderGatewayFilterFactory,它可以从Header中移除指定的包头中的参数,但是这个版本中只能配置一个参数。在application.yml中配置如下所示:

spring:
  cloud:
    gateway:
      routes:
      - id: removerequestheader_route
        uri: http://example.org
        filters:
        - RemoveRequestHeader=X-Request-Foo

在向后面的执行流发送请求之前,它会移除包头中名字为X-Request-Foo的值。

5.12 RemoveResponseHeader GatewayFilter Factory

    这个过滤器的实现类是RemoveResponseHeaderGatewayFilterFactory,它用来从响应的包头中移除指定的参数,如下面配置所示:

spring:
  cloud:
    gateway:
      routes:
      - id: removeresponseheader_route
        uri: http://example.org
        filters:
        - RemoveResponseHeader=X-Response-Foo

在返回客户端之后,它会从响应消息的包头中移除X-Response-Foo参数的值。

 

posted @ 2019-08-30 14:02  王广帅  阅读(2690)  评论(0编辑  收藏  举报