目录

spirngcloud服务网关gateway

概述

在微服务中,几乎所有的微服务架构都需要 网关 的加持,目的就是:让网关统一的挡在所有请求之前,进行 日志管理、 限流、 权鉴、 安全加固 等一些操作。我们可以在 Zuul、Zuul2、Gateway 中进行服务网关的技术选型,我们选择 Gateway。

为什么选择gateway

以前,Zuul 作为默认服务网关被使用,Zuul Github 官网。Zuul 是 Netflix 公司产品,Netflix 认为 Zuul 版本过老需要更新换代,Zuul 停更了。Zuul2 版本开始开发,此时 Spring Cloud 也在等待 Zuul 新版本的开发。 结果:Zuul 的核心开发人员跳槽了,他们对 Zuul 升级到 Zuul2 产生了分歧,导致 Zuul2 一次又一次的推迟,2年都没能开发完成。 Spring 不再继续等 Zuul2 ,Spring 社区便借鉴 Zuul 的思想,开发了自己的 Spring Cloud Gateway。

Zuul2 开发完成,因为自己亲儿子 Gateway 的加持, Spring 也就没有开发 Spring Cloud 和 Zuul2 的整合包。用自己的 Gateway 显然更香,就没有再整合 Zuul2 的必要了。

附官网链接:Spring Cloud Gateway 官网。简单来说 Gateway 就是 Zuul 1.x 的替代品。Gateway 网关在微服务架构中的位置,如下图:

http://img.cana.space/picStore/20201111125811.png

Gateway是什么

Spring Cloud Gateway 是在 Spring Cloud 的一个全新项目,基于 Spring5Spring Boot 2Project Reactor 等技术开发的网关,它旨在为微服务提供一种简单有效的统一的 API 路由管理。

Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Zuul。在 Spring Cloud 2.0 以上版本中,没有对新版本的 Zuul 2.0 以上版本进行集成,使用的使用时 Zuul 1.x 非 Reactor 模式的老版本。

为了提升网关的性能,Spring Cloud Gateway 基于 WebFlux 框架实现,WebFlux 框架底层使用的时高性能 Reactor 模式通信框架 Netty,对于高并发分布式系统有非常大的优势。

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

Gateway的功能

  • 反向代理
  • 鉴权
  • 流量控制
  • 熔断
  • 日志监控

Gateway的特性

  1. 基于 Spring Framework 5、Project Reactor 和 Spring Boot 2.0 进行构建;
  2. 动态路由:能够匹配任何请求属性;
  3. 可以对路由指定 Predicate(断言) 和 Filter(过滤器),易于编写;
  4. 集成 Hystrix 的断路器功能;
  5. 集成 Spring Cloud 服务发现功能(Gateway一样可注册到 Eureka);
  6. 请求限流功能;
  7. 支持路径重写;
  8. Spring 自家产品,更稳定。

Gateway和Zuul的区别

在 Spring Cloud Finchley 正式版之前,Spring Cloud 推荐的网关是 Netflix 提供的 Zuul。在这之后,还是更倾向于推荐使用自己的亲儿子:Spring Cloud Gateway。 它两者区别如下:

  1. Zuul 1.x 是一个基于 阻塞 I/O 的 API 网关
  2. Zuul 1.x 基于 Servlet 2.5 使用阻塞架构,它不支持任何长连接(如:websocket)。Zuul的设计模式和 Nginx 类似,每次 I/O 操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,但是差别是 Nginx 用 C++ 实现,Zuul 用 Java 实现,JVM 本身会有第一次加载较慢的情况,是的 Zuul 的性能相对较差;
  3. Zuul 2.x 理念更先进,但是 Spring Cloud 目前还没有整合。Zuul 2.x 的性能较 Zuul 1.x 有较大提升。在性能方面,根据官方提供的基准测试,Spring Cloud Gateway 的 RPS(每秒请求数)是 Zuul 的 1.6 倍;
  4. Spring Cloud Gateway 建立在 Spring Framework 5、Project Reactor 和 Spring Boot 2.0 之上,使用非阻塞 API
  5. Spring Cloud Gateway 还支持 WebSocket,并且与 Spring 集成会有更好的开发体验。

Gateway三大概念

Route(路由)、Predicate(断言)、Filter(过滤),这三大概念构成了强大的 Gateway。一个web 请求,通过一些匹配条件,最终定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。Predicate 就是我们的匹配条件;而 Filter 就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标 URI,就可以实现一个具体的路由了

Ⅰ、Route(路由)

路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为 true 则匹配该路由

Ⅱ、Predicate(断言)

参考的是 java8 的 java.util.function.Predicate,开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求URL与断言相匹配(true)则进行路由

Ⅲ、Filter(过滤)

指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。类似 过滤器拦截器 的概念。

网关在微服务架构中的位置

http://img.cana.space/picStore/20201111131612.png

Gateway工作流程

http://img.cana.space/picStore/20201111131706.png

Clients make requests to Spring Cloud Gateway. If the Gateway Handler Mapping determines that a request matches a route, it is sent to the Gateway Web Handler. This handler runs the request through a filter chain that is specific to the request. The reason the filters are divided by the dotted line is that filters can run logic both before and after the proxy request is sent. All “pre” filter logic is executed. Then the proxy request is made. After the proxy request is made, the “post” filter logic is run.

客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handlar Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。

Handler 再通过指定的 过滤器链 将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。

Filter中 “pre” 类型的过滤器可以做 参数校验、权限校验、流量监控、日志输出、协议转换等; “post” 类型的过滤器中可以做 响应内容、响应头的修改、日志的输出、流量监控 等有着非常重要 的作用。

Gateway核心逻辑一句话总结:路由转发 + 执行过滤器链

构建网关微服务

  1. 新建moudle,cloud-gateway-gateway9527

  2. pom

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    
    <?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>cloud2020</artifactId>
            <groupId>org.eh</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
       
        <artifactId>cloud-gateway-gateway9527</artifactId>
       
       
        <dependencies>
            <!--注意,网关底层依赖webflux,不要引入web模块否则会报错-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
            <!--网关本身也是一个微服务,需要注册进注册中心-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.eh</groupId>
                <artifactId>cloud-common</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
       
    </project>
    
  3. yml

    我们目前不想暴露8001端口,希望在8001外面套一层9527,生产环境8001会加防火墙,都通过9527来访问

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    server:
      port: 9527
       
    spring:
      application:
        name: cloud-gateway
      cloud:
        gateway:
          routes:
            #路由的ID,没有固定规则但要求唯一,建议配合服务名
            - id: cloud_provider_payment8001_getPaymentById
              #匹配后提供服务的路由地址
              uri: http://localhost:8001
              # 断言,路径相匹配的进行路由
              predicates:
                - Path=/payment/**
            - id: cloud_provider_payment8001_paymentTimeout
              uri: http://localhost:8001
              predicates:
                - Path=/paymentTimeout
       
    eureka:
      instance:
        hostname: cloud-gateway-service
      client:
        service-url:
          register-with-eureka: true
          fetch-registry: true
          defaultZone: http://localhost:7000/eureka
    
  4. 主启动类

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    package com.eh.gateway.gateway;
       
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
       
    @EnableDiscoveryClient
    @SpringBootApplication
    public class GatewayMain9527 {
        public static void main(String[] args) {
            SpringApplication.run(GatewayMain9527.class, args);
        }
    }
    
  5. 测试

    1. 访问 http://localhost:8001/paymentTimeout
    2. 访问 http://localhost:9527/paymentTimeout

网关路由配置的两种方式

第一种就是上面在yml中配置的方式

第二种是在容器中注入RouteLocator,参考官网:Getting Started

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@SpringBootApplication
public class DemogatewayApplication {
	@Bean
	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
		return builder.routes()
			.route("path_route", r -> r.path("/get")
				.uri("http://httpbin.org"))
			.route("host_route", r -> r.host("*.myhost.org")
				.uri("http://httpbin.org"))
			.route("rewrite_route", r -> r.host("*.rewrite.org")
				.filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}"))
				.uri("http://httpbin.org"))
			.route("hystrix_route", r -> r.host("*.hystrix.org")
				.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
				.uri("http://httpbin.org"))
			.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
				.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
				.uri("http://httpbin.org"))
			.route("limit_route", r -> r
				.host("*.limited.org").and().path("/anything/**")
				.filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
				.uri("http://httpbin.org"))
			.build();
	}
}

自己写一个,通过9527网关访问外网的百度新闻国内网址http://news.baidu.com/guonei

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package com.eh.gateway.gateway.config;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("baidu_guonei", r -> r.path("/guonei")
                        .uri("http://news.baidu.com/guonei"))
                .build();
    }
}

运行程序,访问http://localhost:9527/guonei

http://img.cana.space/picStore/20201111144142.png

通过微服务名实现动态路由

之前消费者调提供者,消费者集成ribbon做负载均衡,现在使用网关调服务,我们该如何负载均衡呢?

默认情况下Gatway会根据注册中心注册的服务列表, 以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能,其实底层还是使用的ribbon做的负载均衡,所以也可以像之前一样更换负载均衡算法。

演示

  • cloud-gateway-gateway9527
  • cloud-eureka-single-server7000
  • cloud-provider-payment8001
  • cloud-provider-payment8002

修改yml配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    discovery:
      locator:
        enabled: true
    gateway:
      routes:
        #路由的ID,没有固定规则但要求唯一,建议配合服务名
        - id: cloud_provider_payment8001_getPaymentById
          #匹配后提供服务的路由地址
#          uri: http://localhost:8001
          # lb://表示基于服务注册的负载均衡
          uri: lb://cloud-payment-service
          # 断言,路径相匹配的进行路由
          predicates:
            - Path=/payment/**
        - id: cloud_provider_payment8001_paymentTimeout
#          uri: http://localhost:8001
          uri: lb://cloud-payment-service
          predicates:
            - Path=/paymentTimeout

eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://localhost:7000/eureka

改动点:

  1. uri: lb://cloud-payment-service spring cloud gateway在微服务中自动为我们创建负载均衡的uri
  2. 表示启用discovery.locator.enabled基于注册中心的负载均衡功能

测试:

访问:http://localhost:9527/payment/1,8001/8002两个端口切换

Predicate的使用

Predicate是什么

在我们gateway服务启动的时候查看日志会发现加载了很多RoutePredicateFactory,如下:

http://img.cana.space/picStore/20201111154845.png

Route Predicate Factory官网说明

Spring Cloud Gateway matches routes as part of the Spring WebFlux HandlerMapping infrastructure. Spring Cloud Gateway includes many built-in route predicate factories. All of these predicates match on different attributes of the HTTP request. You can combine multiple route predicate factories with logical and statements.

Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。

Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以进行组合

Spring Cloud Gateway创建Route对象时,使用RoutePredicateFactory创建Predicate对象,Predicate对象可以赋值给Route。Spring Cloud Gateway包含许多内置的Route Predicate Factories。

所有这些谓词都匹配HTTP请求的不同属性。多种谓词工厂可以组合,并通过逻辑and。

常用的Route Predicate

AbstractRoutePredicateFactory (org.springframework.cloud.gateway.handler.predicate)

  • HeaderRoutePredicateFactory (org.springframework.cloud.gateway.handler.predicate)

    1
    2
    3
    4
    5
    6
    7
    8
    
    spring:
      cloud:
        gateway:
          routes:
          - id: header_route
            uri: https://example.org
            predicates:
            - Header=X-Request-Id, \d+
    

    eg:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
        gateway:
          routes:
            #路由的ID,没有固定规则但要求唯一,建议配合服务名
            - id: cloud_provider_payment8001_getPaymentById
              #匹配后提供服务的路由地址
    #          uri: http://localhost:8001
              # lb://表示基于服务注册的负载均衡
              uri: lb://cloud-payment-service
              # 断言,路径相匹配的进行路由
              predicates:
                - Path=/payment/**
                - Header=X-Request-Id, \d+
    

    curl访问

    1
    2
    3
    4
    5
    6
    7
    
    # david @ Davids-Macbook-Pro in ~/my/cana.space [16:06:17]
    $ curl http://localhost:9527/payment/1 -H "X-Request-Id=1"
    {"timestamp":"2020-11-11T08:13:13.789+0000","path":"/payment/1","status":404,"error":"Not Found","message":null,"requestId":"a87a5d21"}%
      
    # david @ Davids-Macbook-Pro in ~/my/cana.space [16:13:13]
    $ curl http://localhost:9527/payment/1 -H "X-Request-Id:123"
    {"code":200,"desc":"查询订单成功,提供服务端口号:8001","data":{"id":1,"serial":"aaabbb01"}}%
    
  • PathRoutePredicateFactory (org.springframework.cloud.gateway.handler.predicate)

  • BeforeRoutePredicateFactory (org.springframework.cloud.gateway.handler.predicate)

  • CloudFoundryRouteServiceRoutePredicateFactory (org.springframework.cloud.gateway.handler.predicate)

  • QueryRoutePredicateFactory (org.springframework.cloud.gateway.handler.predicate)

    1
    2
    3
    4
    5
    6
    7
    8
    
    spring:
      cloud:
        gateway:
          routes:
          - id: query_route
            uri: https://example.org
            predicates:
            - Query=green
    

    请求体中必须含有请求参数green

  • RemoteAddrRoutePredicateFactory (org.springframework.cloud.gateway.handler.predicate)

  • MethodRoutePredicateFactory (org.springframework.cloud.gateway.handler.predicate)

    eg:

    1
    2
    3
    4
    5
    6
    7
    8
    
    spring:
      cloud:
        gateway:
          routes:
          - id: method_route
            uri: https://example.org
            predicates:
            - Method=GET,POST
    
  • CookieRoutePredicateFactory (org.springframework.cloud.gateway.handler.predicate)

    Cookie Route Predicate需要两个参数,一个是Cookie name,一个是正则表达式。如果匹配上则执行路由,否则404

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
        gateway:
          routes:
            #路由的ID,没有固定规则但要求唯一,建议配合服务名
            - id: cloud_provider_payment8001_getPaymentById
              #匹配后提供服务的路由地址
    #          uri: http://localhost:8001
              # lb://表示基于服务注册的负载均衡
              uri: lb://cloud-payment-service
              # 断言,路径相匹配的进行路由
              predicates:
                - Path=/payment/**
                - Cookie=chocolate, ch.p
    

    不带cookie访问

    1
    2
    3
    
    # david @ Davids-Macbook-Pro in ~/my/cana.space [16:04:12]
    $ curl http://localhost:9527/payment/1
    {"timestamp":"2020-11-11T08:05:00.466+0000","path":"/payment/1","status":404,"error":"Not Found","message":null,"requestId":"ceba8c7f"}%
    

    带上cookie访问

    1
    2
    3
    4
    5
    6
    7
    
    # david @ Davids-Macbook-Pro in ~/my/cana.space [16:05:00]
    $ curl http://localhost:9527/payment/1 --cookie "chocolate=ch.p"
    {"code":200,"desc":"查询订单成功,提供服务端口号:8002","data":{"id":1,"serial":"aaabbb01"}}%
      
    # david @ Davids-Macbook-Pro in ~/my/cana.space [16:06:13]
    $ curl http://localhost:9527/payment/1 --cookie "chocolate=ch.p1"
    {"timestamp":"2020-11-11T08:06:17.255+0000","path":"/payment/1","status":404,"error":"Not Found","message":null,"requestId":"a25c88a8"}%
    
  • WeightRoutePredicateFactory (org.springframework.cloud.gateway.handler.predicate)

  • AfterRoutePredicateFactory (org.springframework.cloud.gateway.handler.predicate)

    1
    2
    3
    4
    5
    6
    7
    8
    
    spring:
      cloud:
        gateway:
          routes:
          - id: after_route
            uri: https://example.org
            predicates:
            - After=2017-01-20T17:42:47.789-07:00[America/Denver]
    

    获取当前时区的格式化时间方法:

    1
    2
    3
    4
    5
    6
    
    public static void main(String[] args) {
        ZonedDateTime dateTime = ZonedDateTime.now();
        System.out.println(dateTime);
    }
    // 输出
    2020-11-11T15:58:10.033+08:00[Asia/Shanghai]
    
  • BetweenRoutePredicateFactory (org.springframework.cloud.gateway.handler.predicate)

    eg:

    1
    2
    3
    4
    5
    6
    7
    8
    
    spring:
      cloud:
        gateway:
          routes:
          - id: between_route
            uri: https://example.org
            predicates:
            - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
    
  • HostRoutePredicateFactory (org.springframework.cloud.gateway.handler.predicate)

    域名匹配

    1
    2
    3
    4
    5
    6
    7
    8
    
    spring:
      cloud:
        gateway:
          routes:
          - id: host_route
            uri: https://example.org
            predicates:
            - Host=**.somehost.org,**.anotherhost.org
    
  • ReadBodyPredicateFactory (org.springframework.cloud.gateway.handler.predicate)

小结

说白了,Predicate就是为了实现一组匹配规则, 让请求过来找到对应的Route进行处理

Filter的使用

Filter的使用

Route filters allow the modification of the incoming HTTP request or outgoing HTTP response in some manner. Route filters are scoped to a particular route. Spring Cloud Gateway includes many built-in GatewayFilter Factories.

路由过滤器可用于修改进入的Http请求和返回的Http响应,路由过滤器只能指定路由进行使用。Spring Cloud Gateway内置了多种路由过滤器,他们都由GatewayFilter的工厂类来产生。

Filter生命周期

  • pre
  • post

Filter种类

  • GatewayFilter

    官方提供的GatewayFilter 共 31 个,官网地址:GatewayFilter Factories

    使用示例,这里使用官方提供的AddRequestPameter

    1
    2
    3
    4
    5
    6
    7
    8
    
    spring:
      cloud:
        gateway:
          routes:
          - id: add_request_parameter_route
            uri: https://example.org
            filters:
            - AddRequestParameter=red, blue
    
  • GlobalFilter

    官方提供的GlobalFilter 共 10 个,官网地址:GlobalFilter Factories

自定义过滤器

自定义全局过滤器,可以帮助我们进行 全局日志记录统一网关鉴权 等功能。我们需要定义一个类,并实现 GlobalFilterOrdered 两个接口,重写里面的 filter、order 方法即可。

主要是 filter 方法,编写详细的过滤器逻辑order 方法是用来定义加载过滤器的优先级,返回一个 int 数值,值越小优先级越高。

案例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.eh.gateway.gateway.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Slf4j
@Component
public class MyLoginGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String username = exchange.getRequest().getQueryParams().getFirst("username");
        // 用户登录状态检查
        if (StringUtils.isEmpty(username)) {
            log.error("=========>非法用户");
            // 设置http状态码
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            // 立即返回错误信息给客户端
            return exchange.getResponse().setComplete();
        }
        // 检查通过继续下一个filter
        return chain.filter(exchange);
    }

    // 加载过滤器优先级,越小优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}

测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 不带username
# david @ Davids-Macbook-Pro in ~/my/cana.space [16:35:10]
$ curl http://localhost:9527/payment/1
# 后台报
2020-11-11 16:36:48.961 ERROR 46900 --- [ctor-http-nio-2] c.e.g.g.config.MyLoginGlobalFilter       : =========>非法用户

# 带username
# david @ Davids-Macbook-Pro in ~/my/cana.space [16:36:49]
$ curl http://localhost:9527/payment/1\?username\=david
{"code":200,"desc":"查询订单成功,提供服务端口号:8001","data":{"id":1,"serial":"aaabbb01"}}%

Predicate和Filter区别

  • predicate用于路由匹配,filter用于匹配之后的请求响应过滤
  • predicate基于webflux的handlermapping,filter可以理解成springmvc里的过滤器
  • predicate路由匹配不通过报404,filter可以定义任何状态码