为了账号安全,请及时绑定邮箱和手机立即绑定

SpringCloud-06-Gateway

标签:
Spring Cloud

Spring Cloud 体系内的网关技术主要有 ZuulSpring Cloud Gateway。Zuul 是 Netflix 公司开源的产品,也是 Spring Cloud F版本 之前默认使用的服务网关组件,但是随着 Netflix 公司一系列的停更事件,在 Spring Cloud G版本后已经不建议采用 Zuul 技术,官方建议使用 Spring Cloud Gateway 作为默认的网关技术。

微服务架构中网关在哪里?

1. Spring Cloud Gateway简介

Spring Cloud Gateway 是 Spring 体系内的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。

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

Spring Cloud Gateway 的目标是提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全、监控/指标和限流。

Spring Cloud Gateway 特性

  1. 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
  2. 动态路由
  3. Predicates 和 Filters 作用于特定路由
  4. 集成 Hystrix 断路器
  5. 集成 Spring Cloud DiscoveryClient
  6. 易于编写的 Predicates 和 Filters
  7. 限流
  8. 路径重写

Spring Cloud Gateway 中的相关概念

  • Route(路由):这是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为true,则路由匹配。
  • Predicate(断言):参考的是java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
  • Filter(过滤器):这是org.springframework.cloud.gateway.filter.GatewayFilter的实例,可以在请求被路由前或者之后对请求和响应进行修改。

Spring Cloud Gateway工作原理

客户端向 Spring Cloud Gateway 发出请求。如果 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。 过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。

虚线左边的是 pre 类型过滤器,虚线右边的是 post 类型过滤器,外部请求进来后先经过 pre 类型的过滤器,再发送到代理服务,代理服务响应请求,再次经过 post 过滤器链,最后传递给前端。

Filter在 pre 类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在 post 类型过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等。

Spring Cloud Gateway 与 Zuul 对比

  1. Zuul 是 Netflix 开源的产品,Spring Cloud Gateway 是 Spring 体系内的产品,和 Spring 体系融合更好。
  2. Zuul1 不支持长连接,比如 WebSockets;Spring Cloud Gateway 支持 WebSockets 等协议。
  3. 作为一个服务网关产品,Spring Cloud Gateway 考虑更全面一些,增加了 Predicate、限流等技术。
  4. Zuul1 是基于 Servlet 框架构建,采用的是阻塞和多线程方式,即一个线程处理一次连接请求,这种方式在内部延迟严重、设备故障较多情况下会引起存活的连接增多和线程增加的情况发生。
  5. Spring Cloud Gateway 基于 Spring 5.0、Spring Boot 2.0 和 Project Reactor 开发,采用了 Netty 实现了异步非阻塞模型,占用资源更小,在性能方面更有优势。

2. Spring Cloud Gateway实践

目前项目 006SpringCloud 已提供了几个模块,都是前面文章的demo:

  • eureka注册中心: eureka-server-7001和 eureka-server-7002
  • 服务提供者:provider-payment9001 和 provider-payment9002

服务提供者中都提供了接口 “/payment/get/{id}”,返回的是 CommonResult<Payment> 。

@RestController
@RequestMapping("/payment")
public class PaymentController {
    private Logger logger = LoggerFactory.getLogger(PaymentController.class);

    @Value("${server.port}")
    private String serverPort;

    @GetMapping(value = "/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {

        if (id != null && id % 2 == 0) {
            Payment payment = Payment.newInstance().setSerial(UUID.randomUUID().toString()).setId(id);
            return CommonResult.newInstance().setCode(200).setMessage("查询成功,serverPort:" + serverPort).setData(payment);
        } else {
            return CommonResult.newInstance().setCode(444).setMessage("没有对应记录,查询ID: " + id).setData(null);
        }
    }
    ...
}

目前项目 006SpringCloud 新建模块 gateway-6001,并在 pom.xml 中添加 Spring Cloud Gateway 依赖。

gateway-6001的pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>006SpringCloud</artifactId>
        <groupId>com.xander</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gateway-6001</artifactId>

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

</project>

启动类Gateway6001.java

@SpringBootApplication
@EnableDiscoveryClient
public class Gateway6001 {

    public static void main(String[] args) {
        SpringApplication.run(Gateway6001.class, args);
    }

}

application.yml

server:
  port: 6001

eureka:
  instance:
    hostname: gateway
  client:
    #表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # 集群版-
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

logging:
  level:
#    调整 gateway 包的 log 级别,以查看 Gateway 相关的日志
    org.springframework.cloud.gateway: debug

spring:
  application:
    name: gateway

**需求:**通过gateway-6001网关服务实现路由转发的功能,访问 http://localhost:6001/payment/get/{id} 时会自动转发到地址:http://localhost:9001/payment/get/{id}

Gateway网关路由有两种配置方式:

  1. Spring容器中注入RouteLocator的Bean
  2. 在配置文件yml中配置

2.1 配置方式一:注入RouteLocator的Bean

RouteConfig.java路由配置类

@Configuration
public class RouteConfig {

    /**
     * 自定义 RouteLocator
     * @return
     */
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        //以 /payment/ 开头的uri,都转发给 http://localhost:9001
        return builder.routes()
                //路由id: path_route; 匹配的路径:/payment/**
                .route("path_route", r -> r.path("/payment/**")
                        // 匹配成功转发给:http://localhost:9001
                        .uri("http://localhost:9001"))
                .build();
    }

}

启动服务后,访问 http://localhost:6001/payment/get/2 ,网关转发请求成功。

查看gateway匹配路由以及转发日志,可以看到匹配到了id为path_route的路由:

2021-05-31 17:03:23.942 DEBUG 21720 --- [ctor-http-nio-5] o.s.c.g.h.RoutePredicateHandlerMapping   : Route matched: path_route
2021-05-31 17:03:23.942 DEBUG 21720 --- [ctor-http-nio-5] o.s.c.g.h.RoutePredicateHandlerMapping   : Mapping [Exchange: GET http://localhost:6001/payment/get/2] to Route{id='path_route', uri=http://localhost:9001, order=0, predicate=Paths: [/payment/**], match trailing slash: true, gatewayFilters=[], metadata={}}
2021-05-31 17:03:23.942 DEBUG 21720 --- [ctor-http-nio-5] o.s.c.g.h.RoutePredicateHandlerMapping   : [20f7ce0c-4] Mapped to org.springframework.cloud.gateway.handler.FilteringWebHandler@13f764f7

2.2 配置方式二:在配置文件yml中配置

注释掉RouteLocator Bean

@Configuration
public class RouteConfig {

    /**
     * 自定义 RouteLocator
     * @return
     */
    // @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        //以 /payment/ 开头的uri,都转发给 http://localhost:9001
        return builder.routes()
                //路由id: path_route; 匹配的路径:/payment/**
                .route("path_route", r -> r.path("/payment/**")
                        // 匹配成功转发给:http://localhost:9001
                        .uri("http://localhost:9001"))
                .build();
    }

}

application.yml 中添加配置

spring:
  cloud:
    gateway:
      routes:
        # 路由配置
        - id: payment_route
          # 转发到 http://localhost:9001
          uri: http://localhost:9001
          predicates:
            # 断言,匹配路径 /payment/**
            - Path=/payment/**

重启服务后,访问 http://localhost:6001/payment/get/2 ,网关转发请求成功。

查看gateway匹配路由以及转发日志,可以看到匹配到了id为 payment_route 的路由:

2021-05-31 17:18:13.433 DEBUG 16148 --- [ctor-http-nio-2] o.s.c.g.h.RoutePredicateHandlerMapping   : Route matched: payment_route
2021-05-31 17:18:13.433 DEBUG 16148 --- [ctor-http-nio-2] o.s.c.g.h.RoutePredicateHandlerMapping   : Mapping [Exchange: GET http://localhost:6001/payment/get/2] to Route{id='payment_route', uri=http://localhost:9001, order=0, predicate=Paths: [/payment/**], match trailing slash: true, gatewayFilters=[], metadata={}}
2021-05-31 17:18:13.433 DEBUG 16148 --- [ctor-http-nio-2] o.s.c.g.h.RoutePredicateHandlerMapping   : [524a4282-1] Mapped to org.springframework.cloud.gateway.handler.FilteringWebHandler@4cb1d057

3. 负载均衡

当路由配置中 uri 所用的协议为 lb 时(以 uri: lb://paymentService 为例),gateway 将使用 LoadBalancerClient 把 paymentService 通过 eureka 注册中心拉取的服务列表解析为实际的主机和端口,并进行负载均衡。

application.yml 中修改配置

spring:
  cloud:
    gateway:
      routes:
        # 路由配置
        - id: payment_route
          # 转发到 http://localhost:9001
#          uri: http://localhost:9001
          # lb协议,负载均衡,轮询转发到
          uri: lb://paymentService
          predicates:
            # 断言,匹配路径 /payment/**
            - Path=/payment/**

重启服务,访问 http://localhost:6001/payment/get/2 ,负载均衡已生效

4. 根据 serviceId 转发请求

开启根据 serviceId 转发
spring.cloud.gateway.discovery.locator.enabled=true:是否开启通过注册中心进行路由转发的功能,通过 serviceId 转发到服务,默认为 false。

application.yml 中修改配置

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        # 路由配置
#        - id: payment_route
#          # 转发到 http://localhost:9001
##          uri: http://localhost:9001
#          # lb协议,负载均衡,轮询转发到
#          uri: lb://paymentService
#          predicates:
#            # 断言,匹配路径 /payment/**
#            - Path=/payment/**

      # 开启根据 serviceId 转发,负载均衡也生效
      discovery:
        locator:
          enabled: true

重启服务,访问 http://localhost:6001/PAYMENTSERVICE/payment/get/2 ,根据服务id进行转发,同时负载均衡也生效。


5. Predicate 断言

Predicate 是 Java 8 中引入的一个函数,Predicate 接受一个输入参数,返回一个布尔值结果。

Spring Cloud Gateway 是通过 Spring WebFlux 的 HandlerMapping 做为底层支持来匹配到转发路由,Spring Cloud Gateway 内置了很多 Predicates 工厂,这些 Predicates 工厂通过不同的 HTTP 请求参数来匹配,多个 Predicates 工厂可以组合使用。

注意: 如果有多个路由中配置的 Predicate 都能够匹配到请求,则在配置文件中自上而下只将请求交给第一个匹配到的路由处理。

5.1 通过时间匹配:After、Before、Between

After Route Predicate Factory 采用一个参数——日期时间。在该日期时间之后发生的请求都将被匹配。

application.yml

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

Before Route Predicate Factory 采用一个参数——日期时间。在该日期时间之前发生的请求都将被匹配。

application.yml.

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

Between Route Predicate Factory 有两个参数,datetime1和datetime2。在datetime1和datetime2之间的请求将被匹配。datetime2参数的实际时间必须在datetime1之后。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: http://example.org
        predicates:
        - Between=2017-01-20T17:42:47.789-07:00[Asia/Shanghai], 2017-01-21T17:42:47.789-07:00[Asia/Shanghai]

5.2 Cookie 匹配

Cookie Route Predicate Factory 有两个参数,cookie名称和正则表达式。
断言匹配条件:
请求包含cookie名称并且该cookie的值被正则表达式匹配为true。

application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: http://example.org
        predicates:
        - Cookie=chocolate, ch.p

5.3 Header 匹配

Header Route Predicate Factory 有两个参数,header名称和正则表达式。
断言匹配条件:
请求包含次header名称并且该header的值被正则表达式匹配为true。

application.yml.

spring:
 cloud:
   gateway:
     routes:
     - id: header_route
       uri: http://example.org
       predicates:
       - Header=X-Request-Id, \\d+

5.4 Host 匹配

Host Route Predicate Factory 包括一个参数:host name列表。使用Ant路径匹配规则,.作为分隔符。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org

5.5 Method 请求方法匹配

Method Route Predicate Factory 只包含一个参数: 需要匹配的HTTP请求方式

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: http://example.org
        predicates:
        - Method=GET

所有GET请求都将被路由。

5.6 Path 请求路径匹配

Path Route Predicate Factory 有2个参数: 一个Spring PathMatcher表达式列表和可选的matchOptionalTrailingSeparator标识 .

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://example.org
        predicates:
        - Path=/foo/{segment},/bar/{segment}

例如: /foo/1 or /foo/bar or /bar/baz的请求都将被匹配

URI 模板变量 (如上例中的 segment ) 将以Map的方式保存于ServerWebExchange.getAttributes() key为ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE. 这些值将在[GatewayFilter Factories]使用

可以使用以下方法来更方便地访问这些变量。

Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);

String segment = uriVariables.get("segment");

5.7 Query 请求参数匹配

Query Route Predicate Factory 有2个参数: 必选项参数名 param 和可选项 regexp值匹配的正则表达式.

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: http://example.org
        predicates:
        - Query=baz

则包含了请求参数 baz的都将被匹配。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: http://example.org
        predicates:
        - Query=foo, ba.

如果请求参数里包含foo参数,并且值匹配为ba. 正则表达式,则将会被路由,如:bar and baz

6. Filter 过滤器

Spring Cloud Gateway 的只提供了两个Filter :“pre” 和 “post”。

  • PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现权限管理、安全校验、记录调试信息等。

  • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

Spring Cloud Gateway 的 Filter 分为两种:GatewayFilter 与 GlobalFilter。GlobalFilter 会应用到所有的路由上,而 GatewayFilter 将应用到单个路由或者一个分组的路由上。

Spring Cloud Gateway 内置了 24 种 GatewayFilter 和 9 种GlobalFilter。

下面挑选几个GatewayFilter讲解一下,下面案例都来自Spring Cloud Gateway 官网。

6.1 AddRequestHeader/RemoveRequestHeader 添加和删除请求头

AddRequestHeader 采用header的名称和值作为参数。

案例:对于所有匹配的请求,向请求中添加请求头 x-request-foo:bar
application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: http://example.org
        filters:
        - AddRequestHeader=X-Request-Foo, Bar

RemoveRequestHeader 有一个name参数. 这是要删除的header的名称。
案例:这将在请求被发送到下游之前删除X-Request-Foo header。
application.yml.

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

6.2 AddRequestParameter 添加请求参数

采用请求参数的名称和值作为参数。

案例:对于所有匹配的请求,向请求中添加foo=bar查询参数。
application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_parameter_route
        uri: http://example.org
        filters:
        - AddRequestParameter=foo, bar

6.3 AddResponseHeader/RemoveResponseHeader 添加和删除响应头

AddResponseHeader采用响应header的名称和值作为参数。

案例:对于所有匹配的请求,添加响应头 x-response-foo:bar

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: add\_request\_header_route
        uri: http://example.org
        filters:
        - AddResponseHeader=X-Response-Foo, Bar

RemoveResponseHeader有一个name参数. 这是要删除的header的名称。

案例:这将在返回到网关client之前从响应中删除x-response-foo响应头。

application.yml.

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

6.4 PrefixPath 请求的路径加前缀

PrefixPath GatewayFilter 只有一个 prefix 参数.

案例:这将给所有匹配请求的路径加前缀/mypath。因此,向/hello发送的请求将发送到http://example.org/mypath/hello
application.yml.

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

6.5 RedirectTo 重定向

该过滤器有一个 status 和一个 url参数。status是300类重定向HTTP代码,如301。该URL应为有效的URL,这将是 Location header的值。

案例:这将发送一个302状态码和一个Location:http://acme.org header来执行重定向。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: http://example.org
        filters:
        - RedirectTo=302, http://acme.org

6.6 RewritePath 重写请求路径

包含一个 regexp正则表达式参数和一个 replacement 参数. 通过使用Java正则表达式灵活地重写请求路径。

案例:对于请求路径/foo/bar,将在发出下游请求之前将路径设置为/bar。注意,由于YAML规范,请使用 $\替换 $
application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: rewritepath_route
        uri: http://example.org
        predicates:
        - Path=/foo/**
        filters:
        - RewritePath=/foo/(?<segment>.*), /$\{segment}

6.7 SetPath GatewayFilter Factory

SetPath GatewayFilter Factory 采用 template路径参数。它提供了一种通过允许路径的模板化 segments 来操作请求路径的简单方法。使用Spring Framework中的URI模板,允许多个匹配的segments。

案例:对于一个 /foo/bar请求,在做下游请求前,路径将被设置为/bar
application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: setpath_route
        uri: http://example.org
        predicates:
        - Path=/foo/{segment}
        filters:
        - SetPath=/{segment}

6.8 SetResponseHeader 设置响应头

SetResponseHeader GatewayFilter Factory 包括 namevalue 参数,此GatewayFilter使用给定的名称替换所有名称匹配的header的值,而不是添加。

案例:如果下游服务器响应为X-Response-Foo:1234,则会将其替换为X-Response-Foo:Bar, 这是网关客户端将接收的内容。
application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: setresponseheader_route
        uri: http://example.org
        filters:
        - SetResponseHeader=X-Response-Foo, Bar

6.9 SetStatus 设置响应状态码

SetStatus GatewayFilter Factory 包括唯一的 status参数.必须是一个可用的Spring HttpStatus。它可以是整数值404或字符串枚举NOT_FOUND

案例:HTTP返回码将设置为401.

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: setstatusstring_route
        uri: http://example.org
        filters:
        - SetStatus=BAD_REQUEST
      - id: setstatusint_route
        uri: http://example.org
        filters:
        - SetStatus=401

6.10 StripPrefix 去除请求路径中的节数

StripPrefix GatewayFilter Factory 包括一个parts参数。 parts参数指示在将请求发送到下游之前,要从请求中去除的路径中的节数。

案例:当通过网关发出/name/bar/foo请求时,向nameservice发出的请求将是http://nameservice/foo

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: nameRoot
        uri: http://nameservice
        predicates:
        - Path=/name/**
        filters:
        - StripPrefix=2

6.11 RequestSize 请求大小限制

当请求大小大于允许的限制时,RequestSize GatewayFilter Factory可以限制请求不到达下游服务。过滤器以RequestSize作为参数,这是定义请求的大小限制(以字节为单位)。

案例:当请求因大小而被拒绝时, RequestSize GatewayFilter Factory 将响应状态设置为413 Payload Too Large,并带有额外的header errorMessage

application.yml.

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

下面是一个 errorMessage的例子。
errorMessage : Request size is larger than permissible limit. Request size is 6.0 MB where permissible limit is 5.0 MB

注意: 默认请求大小为5 MB。

7. 自定义 GatewayFilter 和 GlobalFilter

7.1 自定义 GatewayFilter

对于匹配到的请求,不管请求路径是什么,固定转发路径为 /payment/get/2 。

FixedPathGateWayFilter.java

/**
 * Description: 不管请求路径是什么,固定转发路径为 /payment/get/2 的Filter
 *
 * @author Xander
 * datetime: 2021-03-23 20:45
 */
@Component
public class FixedPathGateWayFilter extends AbstractGatewayFilterFactory {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            logger.info("请求路径:"+request.getURI().getPath());
            //新建 ServerHttpRequest 实例,固定转发路径为 /payment/get/2
            ServerHttpRequest newRequest = request.mutate().path("/payment/get/2").build();
            //重新build exchange
            exchange = exchange.mutate().request(newRequest).build();
            logger.info("固定后路径:"+exchange.getRequest().getURI().getPath());
            return chain.filter(exchange);
        };
    }
}

application.yml修改gateway配置

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        # 路由配置
        - id: payment_route
          # 转发到 http://localhost:9001
#          uri: http://localhost:9001
          # lb协议,负载均衡,轮询转发到
          uri: lb://paymentService
          predicates:
            # 断言,匹配路径 /payment/**
            - Path=/payment/**
          filters:
            # 不管请求路径是什么,固定转发路径为 /payment/get/2 的Filter
            # 配置自定义GateWayFilter
            - name: FixedPathGateWayFilter

      # 开启根据 serviceId 转发,负载均衡也生效
#      discovery:
#        locator:
#          enabled: true

只要发送被 /payment/** 匹配的请求,都会转发到 /payment/get/2

日志打印:

2021-05-31 21:13:32.599 DEBUG 20548 --- [ctor-http-nio-2] o.s.c.g.h.RoutePredicateHandlerMapping   : Route matched: payment_route
2021-05-31 21:13:32.599 DEBUG 20548 --- [ctor-http-nio-2] o.s.c.g.h.RoutePredicateHandlerMapping   : Mapping [Exchange: GET http://localhost:6001/payment/abc] to Route{id='payment_route', uri=lb://paymentService, order=0, predicate=Paths: [/payment/**], match trailing slash: true, gatewayFilters=[[com.xander.gateway.filters.FixedPathGateWayFilter$$Lambda$607/1916822932@2ee00c8e, order = 1]], metadata={}}
...
2021-05-31 21:13:32.600  INFO 20548 --- [ctor-http-nio-2] c.x.g.filters.FixedPathGateWayFilter     : 请求路径:/payment/abc
2021-05-31 21:13:32.602  INFO 20548 --- [ctor-http-nio-2] c.x.g.filters.FixedPathGateWayFilter     : 固定后路径:/payment/get/2

7.2 自定义 GlobalFilter

我们可以通过实现 GlobalFilter 接口来定义一个全局过滤器,并且注入一个自定义 GlobalFilter的Bean到Spring容器中,全局过滤器即可生效,作用与所有的Route。
如果同时配置了 GateWayFilter 和 GlobalFilter,默认情况下GateWayFilter作用顺序优先于GlobalFilter,我们可以通过实现 Ordered接口来修改过滤器的作用顺序。

下面例子,新建一个全局GlobalFilter,打印请求日志。

LogGlobalFilter.java

@Component
public class LogGlobalFilter implements GlobalFilter, Ordered {
    Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        //打印请求uri
        logger.info("全局过滤器LogGlobeFilter:" + request.getURI().getPath());
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        // order: 加载顺序,数值越小,优先级越高
        return 0;
    }
}

访问 http://localhost:6001/payment/abc, 可以看到全局过滤器LogGlobeFilter的日志打印

2021-05-31 21:21:13.481  INFO 11032 --- [ctor-http-nio-2] c.x.gateway.filters.LogGlobalFilter      : 全局过滤器LogGlobeFilter:/payment/abc
2021-05-31 21:21:13.481  INFO 11032 --- [ctor-http-nio-2] c.x.g.filters.FixedPathGateWayFilter     : 请求路径:/payment/abc
2021-05-31 21:21:13.487  INFO 11032 --- [ctor-http-nio-2] c.x.g.filters.FixedPathGateWayFilter     : 固定后路径:/payment/get/2

代码:
github.com/wengxingxia/006SpringCloud.git

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消