一、关于

SpringCloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 2.0之前的非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。

Spring Cloud Gateway特性如下:

  • 基于Spring Framework 5、Project Reactor和Spring Boot 2.0
  • 能够在任何请求属性上进行路由匹配
  • 针对路由的断言和过滤器
  • 集成断路器
  • 集成Spring Cloud DiscoveryClient
  • 易于编写断言和过滤器
  • 支持限速
  • 支持路径重写

Spring Cloud Gateway存在三个主要的概念:

  • Route:路由,网关的基本组成部分。它由一个ID、一个目标URI、一个断言集合和一个过滤器集合定义。如果聚合断言为真,则匹配路由。
  • Predicate:断言,可以匹配来自HTTP请求的任何内容,例如头信息或参数
  • Filter:过滤器,可以在发送请求之前或之后修改请求和响应

本文代码仓库地址:https://github.com/lazyrabb1t/rabb-springcloud-demo

二、使用

2.1 配置断言和过滤器

2.1.1 使用配置文件配置

配置文件有两种配置的方法,简写以及完整配置,如下所示:

application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: after_route_simple
        uri: https://example.org
        predicates:
        - Cookie=mycookie,mycookievalue
      - id: after_route
        uri: https://example.org
        predicates:
        - name: Cookie
          args:
            name: mycookie
            regexp: mycookievalue
2.1.2 使用代码配置
@Configuration
public class RouteConfig {
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("path_route", r -> r.path("/blog")
                        .filters(filter -> filter.addRequestHeader("name", "rabb"))
                        .uri("https://blog.lazyrabbit.xyz"))
                .build();
    }
}

2.2 断言

Spring Cloud Gateway包括许多内置的路由断言工厂。所有这些断言都匹配HTTP请求的不同属性,同时可以将多个路由断言工厂与逻辑和语句组合在一起。

各种 Predicates 同时存在于同一个路由时,请求必须同时满足所有的条件才被这个路由匹配。

一个请求满足多个路由的断言条件时,请求只会被首个成功匹配的路由转发

2.2.1 After 断言

匹配发生在指定日期时间之后的请求

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]
2.2.2 Before 断言

匹配发生在指定日期时间之前的请求

spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: https://example.org
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]
2.2.3 Between 断言

匹配发生在指定日期时间之间的请求

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]

Cookie断言接受两个参数,Cookie名称和regexp(这是一个Java正则表达式)。匹配具有给定名称且其值与正则表达式匹配的cookie,如下将匹配一个包含cookie名称为chocolate且其值匹配正则ch.p的请求:

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p
2.2.5 Header 断言

匹配指定header,配置与Cookie 断言类型

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+
2.2.6 Host 断言

匹配Host头,多个逗号分割

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org
2.2.7 Method 断言

匹配HTTP方法,多个逗号分割

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST
2.2.8 Path断言

匹配请求路径

spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment},/blue/{segment}
2.2.9 Query 断言

匹配query参数

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=green
        - Query=red, gree.
2.2.10 RemoteAddr 断言

匹配源地址

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24
2.2.11 Weight 断言

可以根据配置的权重分发请求

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

2.3 过滤器

路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路由过滤器的作用域是特定的路由。Spring Cloud Gateway包括许多内置的GatewayFilter factory。

2.3.1 AddRequestHeader 过滤器

添加请求头

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-red, blue
2.3.2 AddRequestParameter 过滤器

添加query参数

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_parameter_route
        uri: https://example.org
        filters:
        - AddRequestParameter=red, blue
2.3.3 AddResponseHeader 过滤器

添加响应头

spring:
  cloud:
    gateway:
      routes:
      - id: add_response_header_route
        uri: https://example.org
        filters:
        - AddResponseHeader=X-Response-Red, Blue
2.3.4 DedupeResponseHeader 过滤器

处理重复的请求头,

spring:
  cloud:
    gateway:
      routes:
      - id: dedupe_response_header_route
        uri: https://example.org
        filters:
        - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
2.3.5 CircuitBreaker 过滤器

断路器

spring:
  cloud:
    gateway:
      routes:
      - id: circuitbreaker_route
        uri: https://example.org
        filters:
        - CircuitBreaker=myCircuitBreaker
2.3.6 FallbackHeaders 过滤器

添加断路器异常执行细节

spring:
  cloud:
    gateway:
      routes:
      - id: ingredients
        uri: lb://ingredients
        predicates:
        - Path=//ingredients/**
        filters:
        - name: CircuitBreaker
          args:
            name: fetchIngredients
            fallbackUri: forward:/fallback
      - id: ingredients-fallback
        uri: http://localhost:9994
        predicates:
        - Path=/fallback
        filters:
        - name: FallbackHeaders
          args:
            executionExceptionTypeHeaderName: Test-Header
2.3.7 MapRequestHeader 过滤器

修改指定请求头的值

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-red, blue
2.3.8 PrefixPath 过滤器

添加路径前缀

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: https://example.org
        filters:
        - PrefixPath=/mypath
2.3.9 PreserveHostHeader 过滤器

保留Host请求头

spring:
  cloud:
    gateway:
      routes:
      - id: preserve_host_route
        uri: https://example.org
        filters:
        - PreserveHostHeader
2.3.10 RequestRateLimiter 过滤器

springcloudgateway提供了一个基于redis以及了令牌桶算法的实现类,首先需要添加spring-boot-starter-data-redis-reactive依赖

然后定义限流策略,这里分别定义里基于query参数,请求地址以及请求路径的限流策略:

@Configuration
public class RedisRateLimiterConfig {
    @Bean
    KeyResolver userKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("username"));
    }

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

    @Bean
    KeyResolver apiKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getPath().value());
    }

}

最后配置一个路由:

spring:
  cloud:
    gateway:
      routes:
        - id: requestratelimiter_route
          uri: http://localhost:10000/
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
                redis-rate-limiter.requestedTokens: 10
                key-resolver: "#{@userKeyResolver}"
          predicates:
            - Method=GET

以下是三个需要配置的参数

  • redis-rate-limiter.restishrate属性是指在没有任何请求被丢弃的情况下,每秒允许用户执行的请求数。这是令牌桶被填充的速率
  • redis-rate-limiter.burstCapacity属性是用户在一秒钟内允许做的最大请求数。这是令牌桶可以容纳的令牌数量,将此值设置为0将阻止所有请求
  • redis-rate-limiter。requestedTokens属性表示一个请求所花费的令牌数。这是从桶中为每个请求获取的令牌数量,默认值为1
2.3.11 RedirectTo 过滤器

重定向

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: https://example.org
        filters:
        - RedirectTo=302, https://acme.org
2.3.12 RemoveRequestHeader 过滤器

移除请求头

spring:
  cloud:
    gateway:
      routes:
      - id: removerequestheader_route
        uri: https://example.org
        filters:
        - RemoveRequestHeader=X-Request-Foo
2.3.13 RemoveRequestParameter 过滤器

移除请求参数

spring:
  cloud:
    gateway:
      routes:
      - id: removerequestparameter_route
        uri: https://example.org
        filters:
        - RemoveRequestParameter=red
2.3.14 RemoveResponseHeader 过滤器

移除响应头

spring:
  cloud:
    gateway:
      routes:
      - id: removeresponseheader_route
        uri: https://example.org
        filters:
        - RemoveResponseHeader=X-Response-Foo
2.3.15 RewritePath 过滤器

根据正则重写路径

spring:
  cloud:
    gateway:
      routes:
      - id: rewritepath_route
        uri: https://example.org
        predicates:
        - Path=/red/**
        filters:
        - RewritePath=/red/?(?<segment>.*), /$\{segment}
2.3.16 RewriteLocationResponseHeader 过滤器

重写Location response header

spring:
  cloud:
    gateway:
      routes:
      - id: rewritelocationresponseheader_route
        uri: http://example.org
        filters:
        - RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,
2.3.17 RewriteResponseHeader 过滤器

重写响应头

spring:
  cloud:
    gateway:
      routes:
      - id: rewriteresponseheader_route
        uri: https://example.org
        filters:
        - RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=***
2.3.18 SaveSession 过滤器

转发前保存会话状态

spring.cloud.gateway.filter.secure-headers.disable=x-frame-options,strict-transport-security
2.3.19 SecureHeaders 过滤器

添加了安全相关请求头

spring.cloud.gateway.filter.secure-headers.disable=x-frame-options,strict-transport-security
2.3.20 SetPath 过滤器

设置请求路径

spring:
  cloud:
    gateway:
      routes:
      - id: setpath_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment}
        filters:
        - SetPath=/{segment}
2.3.21 SetRequestHeader 过滤器

设置请求头

spring:
  cloud:
    gateway:
      routes:
      - id: setrequestheader_route
        uri: https://example.org
        filters:
        - SetRequestHeader=X-Request-Red, Blue
2.3.22 SetResponseHeader 过滤器

设置响应头

spring:
  cloud:
    gateway:
      routes:
      - id: setresponseheader_route
        uri: https://example.org
        filters:
        - SetResponseHeader=X-Response-Red, Blue
2.3.23 SetStatus 过滤器

设置响应状态

spring:
  cloud:
    gateway:
      routes:
      - id: setstatusstring_route
        uri: https://example.org
        filters:
        - SetStatus=BAD_REQUEST
      - id: setstatusint_route
        uri: https://example.org
        filters:
        - SetStatus=401
2.3.24 StripPrefix 过滤器

剥离请求路径

spring:
  cloud:
    gateway:
      routes:
      # /name/blue/red -> /red
      - id: nameRoot
        uri: https://nameservice
        predicates:
        - Path=/name/**
        filters:
        - StripPrefix=2
2.3.25 Retry 过滤器

重试

spring:
  cloud:
    gateway:
      routes:
      - id: retry_test
        uri: http://localhost:8080/flakey
        predicates:
        - Host=*.retry.com
        filters:
        - name: Retry
          args:
            retries: 3
            statuses: BAD_GATEWAY
            methods: GET,POST
            backoff:
              firstBackoff: 10ms
              maxBackoff: 50ms
              factor: 2
              basedOnPreviousValue: false
2.3.26 RequestSize 过滤器

限制请求大小

spring:
  cloud:
    gateway:
      routes:
      - id: request_size_route
        uri: http://localhost:8080/upload
        predicates:
        - Path=/upload
        filters:
        - name: RequestSize
          args:
            maxSize: 5000000
2.3.27 SetRequestHostHeader 过滤器

设置请求host头

spring:
  cloud:
    gateway:
      routes:
      - id: set_request_host_header_route
        uri: http://localhost:8080/headers
        predicates:
        - Path=/headers
        filters:
        - name: SetRequestHostHeader
          args:
            host: example.org
2.3.28 ModifyRequestBody 过滤器

修改请求体

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")
            .filters(f -> f.prefixPath("/httpbin")
                .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
                    (exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))
        .build();
}

static class Hello {
    String message;

    public Hello() { }

    public Hello(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
2.3.29 ModifyResponseBody 过滤器

修改响应体

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
            .filters(f -> f.prefixPath("/httpbin")
                .modifyResponseBody(String.class, String.class,
                    (exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri))
        .build();
}
2.3.30 Token Relay 过滤器

将OAuth2访问令牌转发到下游它所代理的服务

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
            .route("resource", r -> r.path("/resource")
                    .filters(f -> f.tokenRelay())
                    .uri("http://localhost:9000"))
            .build();
}
2.3.31 CacheRequestBody 过滤器

缓存请求体

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("cache_request_body_route", r -> r.path("/downstream/**")
            .filters(f -> f.prefixPath("/httpbin")
                .cacheRequestBody(String.class).uri(uri))
        .build();
}
2.3.32 Default Filters

为所有路由添加过滤器

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

2.4 自定义过滤器

Spring Cloud Gateway过滤器可以在请求执行前和执行后进行逻辑处理,多个过滤器同时生效时根据其顺序进行处理,顺序号大的其前置处理逻辑排在后面执行,后置处理逻辑排在前面执行。

2.4.1 全局过滤器

自定义全局过滤器需要实现GlobalFilter以及Ordered,如下:

@Slf4j
public class TokenFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("TokenFilter前置逻辑");
//        String token = exchange.getRequest().getHeaders().getFirst("token");
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        log.info("token:{}", token);
        if (StringUtils.hasText(token)) {
            return chain.filter(exchange).then(Mono.fromRunnable(() ->
            {
                log.info("TokenFilter后置逻辑");
            }));
        } else {
            log.warn("未获取到token");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

然后注册到Spring容器中即对所有请求生效:

@Configuration
public class FilterConfig {
    @Bean
    public GlobalFilter tokenFilter() {
        return new TokenFilter();
    }
}
2.4.2 局部过滤器

自定义局部过滤器需要实现GatewayFilter以及Ordered,如下:

@Slf4j
public class TimeFilter implements GatewayFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("TimeFilter前置逻辑");
        String path = exchange.getRequest().getURI().getPath();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        return chain.filter(exchange).then(Mono.fromRunnable(() ->
        {
            stopWatch.stop();
            long lastTaskTimeMillis = stopWatch.getLastTaskTimeMillis();
            log.info("请求地址:{},耗时{}ms", path, lastTaskTimeMillis);
            log.info("TimeFilter后置逻辑");
        }));

    }

    @Override
    public int getOrder() {
        return 0;
    }
}

然后注册一个过滤器工厂:

@Component
public class TimeGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
    @Override
    public GatewayFilter apply(Object config) {
        return new TimeFilter();
    }
}

最后在路由中添加如下配置:

spring:
  cloud:
    gateway:
      routes:
        - id: rabb_blog_route
          uri: https://blog.lazyrabbit.xyz
          filters:
            - Time
          predicates:
            - Query=blog

2.5 通过服务发现配置路由

以eureka为例,引入eureka依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

添加配置:

spring:
  cloud:
    gateway:
      routes:
        - id: prefixpath_route
          uri: lb://rabb-eureka-provider #此处需要使用lb协议
          predicates:
            - Path=/filter/**
          filters:
            - RewritePath=/filter/?(?<segment>.*), /$\{segment}
            - Time
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能
          lower-case-service-id: true #使用小写服务名,默认是大写
eureka:
  client:
    service-url:
      defaultZone: http://localhost:10002/eureka

然后网关就可以用注册到eureka中的服务的名称来作为路径进行调用其服务接口

参考

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/

https://www.cnblogs.com/crazymakercircle/p/11704077.html#autoid-h2-18-0-2