第二周_R-SpringCloud Gateway-二

87 阅读8分钟

内容来源:

docs.spring.io/spring-clou…

接着上一篇文章:SpringCloud Gateway-上

简单的实战代码也基于上一篇,搭建一个简单的 SpringCloud Gateway 项目;再新启一个服务,提供一个可调用的接口即可。

网关过滤工厂

GatewayFilter Factories:允许以某种方式修改传入 http 请求或传出 http 响应

AddRequestHeader

匹配下面的路由之后,到达下一个服务会在 header 中加上 red=value 的值

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/redis/** # path路由规则
          filters:
            - AddRequestHeader=red,blue

也可以使用路径添加 Header

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/redis/{segment} # path路由规则
          filters:
            - AddRequestHeader=red,blue-{segment}

AddRequestParameter

给匹配的路由添加一个参数:

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/redis/** # path路由规则
          filters:
            - AddRequestParameter=red,blue

同样也适用于路径匹配

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/redis/{tt} # path路由规则
          filters:
            - AddRequestParameter=red,blue-{tt}

也适用于 host 匹配过滤

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_parameter_route
        uri: https://example.org
        predicates:
        - Host: {segment}.myhost.org
        filters:
        - AddRequestParameter=foo, bar-{segment}

AddResponseHeader

适用范围同上,比如路径/host

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/redis/{tt} # path路由规则
          filters:
            - AddResponseHeader=red,blue-{tt}

DedupeResponseHeader

跨域配置的时候防止重复,比如一般具体的服务后端会配置跨域;然后网关这边也会配置统一的跨域,那么不设置就会返回重复。

而且网关的设置会覆盖下面服务的设置:所以只会返回网关的这个设置

CircuitBreaker

短路器,用于熔断降级的。

<dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/redis/** # path路由规则
          filters:
            - name: CircuitBreaker
              args:
                name: myCircuitBreaker
                fallbackUri: forward:/redis/myfallback

配置 5s 超时:如果服务 5s 还没响应,网关就会抛出超时

@Configuration
public class CustomizeCircuitBreakerConfig {
    
    @Bean
    public Customizer<ReactiveResilience4JCircuitBreakerFactory> defaultCustomizer() {
        return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
                .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults())
                .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(5)).build()).build());
    }
    
}

如果没有指定 fallbackUri :网关会抛出自定义异常。指定了就会走指定的 uri

MapRequestHeader

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/redis/** # path路由规则
          filters:
            - MapRequestHeader=Blue, X-Request-Red

当请求的时候,如果 Blue 没有传值,X-Request-Red 传值 ==》服务只有 X-Request-Red 的值

当请求的时候,如果 Blue 传值,X-Request-Red 没有传值 ==》两个都有值,只不过是一样的

当请求的时候,如果 Blue 没有传值,X-Request-Red 没有传值 ==》两个都没有值

PrefixPath

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/** # path路由规则
          filters:
            - PrefixPath=/mypath

访问到服务的接口会加上 「/mypath」路径

PreserveHostHeader

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/** # path路由规则
          filters:
            - PreserveHostHeader

决定是否发送原始主机 host ;没有参数

RequestRateLimiter

这里使用的是 Token Bucket Algorithm 「令牌桶」算法。

常见的限流算法

计数器算法

一般限制一秒钟能够通过的请求数,比如限流 qps 为100:实现思路就是从第一个请求进来开始计时,在接下来的1s内,每来一个请求,就把计数器加 1 ,累计到 100 ,那么后续请求全部被拒绝。等 1s 结束之后,把计数器恢复成 0 ,重新开始计数。

提示:AtomicLong#incrementAndGet()

弊端:如果 1s 的前 10ms 已经有100个请求,那么后面的 990ms 只能拒绝请求,这种现象:突刺现象。

漏桶算法

为了消除突刺现象,可采用漏桶算法。类似漏斗,不管上面的水流多大,下面流出的速度保持不变。相当于处理速度恒定,多余的请求会保存在桶中,在多余的就会丢弃。

提示:准备一个队列,用于保存请求,另外通过一个线程池来定期从队列中获取请求并执行,可以一次性获取多个并发执行。

弊端:无法应对短时间的突发流量。

令牌桶算法

在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择等待或者直接拒绝。

放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃,所以存在桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行「相当于可以瞬间抵挡大请求的能力,比如有 100 个令牌了,那么可以瞬间接收 10 个请求;但是漏桶算法就不行」。

提示:准备一个队列,用来保存令牌,另外通过一个线程池定期生成令牌放到队列中,每来一个请求,就从队列中获取一个令牌,并继续执行。

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

限流配置:如果被限流了,返回:Status Code: 429 Too Many Requests

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/redis/** # path路由规则
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 1  #令牌桶每秒允许填充的速率
                redis-rate-limiter.burstCapacity: 1  #令牌桶总容量(一秒内可执行的最大请求数)
#                redis-rate-limiter.requestedTokens: 1 #一个请求需要多少令牌。这是每个请求从存储桶中获取的令牌数,默认为1.
                key-resolver: "#{@pathKeyResolver}" # 使用 SpEL 表达式按名称引用 bean
  redis:
    host: xxx
    password: xx
    database: 3
@Configuration
public class KeyResolverConfiguration {
    /**
     * 路径限流
     * @return
     */
//    @Bean
//    public KeyResolver pathKeyResolver()
//    {
//        return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
//    }

    /**
     * 参数限流
     */
//    @Bean
//    KeyResolver userKeyResolver() {
//        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
//    }

    /**
     * ip限流
     * @return
     */
    @Bean
    public KeyResolver ipKeyResolver()
    {
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
    }
}

此时访问接口:redis 里面会出现如下的 key-value 值

request_rate_limiter.{xxx}.timestamp

request_rate_limiter.{xxx}.tokens

浏览器中测试调用,当达到临界值,会返回报错:

429 Too Many Requests

RedirectTo

重定向地址:需要两个参数:

  • status:一个 300 系列的重定向 HTTP 代码
  • url:有效的 URL
spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/redis/** # path路由规则
          filters:
            - StripPrefix=1
            - RedirectTo=302,https://www.baidu.com

此时访问 http://localhost:8088/redis/gateway 会跳转到 百度

RmoveRequestHeader

移除 Header 中的值

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/redis/** # path路由规则
          filters:
            - StripPrefix=1
            - RmoveRequestHeader=Blue
            - RemoveResponseHeader=X-Response-Foo

RemoveResponseHeader

移除返回的 header 参数

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/redis/** # path路由规则
          filters:
            - StripPrefix=1
            - RemoveResponseHeader=X-Response-Foo

RemoveRequestParameter

移除指定请求参数

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/redis/** # path路由规则
          filters:
            - StripPrefix=1
            - RemoveRequestParameter=red

RewritePath

和 - StripPrefix=1 效果一致,去掉了 redis 路径

spring:
  application:
    name: test-gateway
  cloud:
    gateway:
      enabled: true # 默认配置是 true ,不需要网关则配置 false
      routes:
        - id: test-gateway # 指定系统id
          uri: http://localhost:8080 # 系统访问地址
          predicates:
            - Path=/redis/** # path路由规则
          filters:
            - RewritePath=/redis/?(?<segment>.*), /${segment}

SaveSession

这在使用类似 Spring Session 的懒惰数据存储时特别有用,因为你需要确保在进行转发调用前已经保存了会话状态。

 filters:
     - SaveSession

SetRequestHeader

设置 header 参数,而不是添加

同理:

  • SetResponseHeader
  • SetStatus
  • StripPrefix=2:去掉 URI 前面 2 个参数,表示要剥离的数量

Default Filters

添加一个过滤器,应用于所有路由

spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Red, Default-Blue
      - PrefixPath=/httpbin