微服务-API网关gateway


1. 为什么放弃Zuul

spring-cloud之前整合的Netflix公司的Zuul网关用的好好的 , 为什么spring官方又推荐使用gateway网关呢? 有以下几点原因.

(1) 上一代Zuul-1.x已经停止更新维护, 意味着bug没有更好的解决.

(2) Zuul-1.x 采用的是Tomcat容器,是一个基于阻塞 I/ O的 API网关, 使用Servlet 2.5阻塞架构, 它不支持任何长连接(如 WebSocket).

  • Zuul 的设计模式和Nginx较像,每次 I/ O 操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,但是差别是
    Nginx 用C++ 实现,Zuul 用 Java 实现,而 JVM 本身会有第一次加载较慢的情况,使得Zuul 的性能相对较差。

  • Zuul 使用Servlet 2.5的阻塞式I/O模型, 而servlet由servlet container进行生命周期管理。
    container启动时构造servlet对象并调用servlet#init()进行初始化;
    container运行时接受请求,并为每个请求分配一个线程(一般从线程池中获取空闲线程)然后调用service()。
    container关闭时调用servlet#destory()销毁servlet;
    在这里插入图片描述

  • 上述模式存在的缺点
    servlet是一个简单的网络IO模型,当请求进入servlet container时,servlet container就会为其绑定一个线程,在并发不高的场景下
    这种模型是适用的。但是一旦高并发(比如 jmeter压测),线程数量就会上涨,而线程资源代价是昂贵的, 上下文频繁切换加大内
    存消耗, 严重影响处理请求的时间。在一些简单业务场景下,不希望为每个request分配一个线程,只需要1个或几个线程就能应
    对极大并发的请求,这种业务场景下servlet模型没有优势.

  • 所以Zuul 1.X这种基于servlet之上的一个阻塞式处理模型的网关,就是一个servlet(DispatcherServlet)处理所有request请求,
    servlet阻塞模型存在无法摆脱的弊端.

(3) 虽然Netflix后面出了Zuul 2.x, 基于Netty非阻塞架构和支持长连接,性能较 Zuul-1.x有很大提升, 但SpringCloud官方目前还没有整合Zuul 2.x. 且Zuul 2.x的更新没有跟上市场的需求, 以致于后来者gateway居上. 就像当年SpringCloud抢占Dubbo市场一样.

在SpringCloud Finchley 正式版之前,Spring Cloud 推荐的网关是 Netflix 提供的Zuul, 之后推荐使用gateway.
在这里插入图片描述

2. 为什么选择gateway

(1) gateway 基于Spring Framework 5、 Project Reactor 和 Spring Boot 2.0构建,使用的非阻塞式 API。

(2) 在性能方面,根据官方提供的基准测试,Spring Cloud Gateway`的 RPS(每秒请求数)是Zuul 的 1. 6 倍。

(3) Spring Cloud Gateway支持长连接WebSocket, 并且与Spring紧密集成拥有更好的开发体验.

  • 在Servlet3.1之后有了异步非阻塞的支持。而WebFlux是一个典型非阻塞异步的框架,它的核心是基于Reactor的相关API实现
    的。相对于传统的web框架(struts2,springmvc只能运行在Servlet容器)来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上。非阻塞式+函数式编程(Spring5让你必须使用java8).
  • Spring WebFlux > 是 Spring 5.0 引入的新的响应式框架,区别于 Spring MVC,它不需要依赖Servlet API,它是完全异步非阻塞
    的,并且基于 Reactor 来实现响应式流规范。
  • 所以在多方面综合考虑下, Gateway 是很理想的网关选择。

3. gateway能干嘛

gateway可以支持反向代理, 鉴权, 流量控制, 熔断, 日志监控等功能。 gateway的这些功能实现可以下面几点优势特性:

  • 动态路由:能够匹配任何请求属性;
  • 可以对路由指定 Predicate(断言)和 Filter(过滤器);
  • 集成Hystrix的断路器功能;
  • 集成 Spring Cloud 服务发现功能;
  • 易于编写的 Predicate(断言)和 Filter(过滤器);
  • 请求限流功能;
  • 支持路径重写。
    在这里插入图片描述

    4. gateway三大核心

    4.1 Route(路由)

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

    4.2 Predicate(断言)

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

    参考的是Java8的java.util.function.Predicate

4.3 Filter(过滤)

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

简单理解:
web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。predicate就是我们
的匹配条件;而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了.
在这里插入图片描述

5. Gateway工作流程

原理图
在这里插入图片描述

  • 客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。
  • Gateway Web Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
  • Filter在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,
  • Filter在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。

    简单理解:
    路由转发+执行过滤器链

6. 最佳实践

6.1 创建网关微服务

新增网关微服务模块[cloud-gateway-gateway-9527]

6.1.1 依赖

<?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>atguigu-cloud-2020</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-gateway-gateway-9527</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>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--一般基础配置类-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </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>

6.1.2 编写配置

server:
  port: 9527
spring:
  application:
    name: cloud-gateway
eureka:
  instance:
    hostname: localhost
    instance-id: cloud-gateway-9527
    prefer-ip-address: true
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url: 
      defaultZone: http://www.eureka01.com:7001/eureka,http://www.eureka02.com:7002/eureka
# gateway网关路由配置下面再详细介绍

6.1.3 编写启动类

com.atguigu.springcloud.GateWayApplication

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * 类描述:spring-cloud-gateway网关微服务启动类
 * @Author wang_qz
 * @Date 2021/11/17 20:22
 * @Version 1.0
 */
@SpringBootApplication
@EnableEurekaClient
public class GateWayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GateWayApplication.class);
    }
}

启动网关微服务, 查看eureka注册中心, 已经成功注册.
在这里插入图片描述

6.1.4 路由配置

Gateway网关路由有两种配置方式. 代码配置方式和l配置文件配置方式.

6.1.4.1 代码配置路由

先来看官网案例介绍.
在这里插入图片描述
大概意思,就是使用Java配置类方式, 创建路由direct-route, 当请求10.1.1.1时, 路由转发到实际的服务地址https://downstream1.
下面我们自己来写一个案例:    当请求url包含/guonei时, 路由转发到http://news.baidu.com/guonei.
com.atguigu.springcloud.config.GateWayConfig

package com.atguigu.springcloud.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;

/**
 * 类描述:gateway网关微服务配置类
 * 配置了一个id为route-name的路由规则,当访问地址 http://localhost:9527/guonei时会自动转发到地址:http://news.baidu.com/guonei
 * @Author wang_qz
 * @Date 2021/11/18 20:01
 * @Version 1.0
 */
@Configuration
public class GateWayConfig {
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("path_route_atguigu", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
        return routes.build();

    }

    @Bean
    public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("path_route_atguigu2", r -> r.path("/guoji").uri("http://news.baidu.com/guoji")).build();
        return routes.build();
    }

}

启动网关微服务应用后, 访问http://localhost:9527/guonei, 成功路由转发到http://news.baidu.com/guonei.
在这里插入图片描述
在这里插入图片描述

6.1.4.2 配置文件配置路由

在application.yml配置文件中添加gateway路由配置.

spring:
  application:
    name: cloud-gateway
  # 网关配置
  cloud:
    gateway:
      routes:
        - id: payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001 #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/get/** # 断言,路径相匹配的进行路由
        - id: payment_route2
          uri: http://localhost:8001
          predicates:
            - Path=/payment/lb/**

上面配置的含义, 是当请求url包含/payment/get/payment/lb/时, 路由转发到实际服务提供地址 http://localhost:8001/payment/gethttp://localhost:8001/payment/lb.
重启微服务应用后, 通过gateway网关访问
http://localhost:9527/payment/get/1
在这里插入图片描述
http://localhost:9527/payment/lb
在这里插入图片描述

6. 2 动态路由

上面的配置虽然已经实现了路由转发的功能, 但是将服务提供者的主机地址写死在路由配置中显得累赘, 如果来上百个服务提供者, 且以集群方式部署, 无法实现负载均衡. gateway提供了动态路由配置, 可以根据微服务实例名称从注册中心获取可用的服务进行路由转发操作.

server:
  port: 9527
spring:
  application:
    name: cloud-gateway
  # 网关配置
  cloud:
    gateway:
      routes:
        - id: payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #          uri: http://localhost:8001 #匹配后提供服务的路由地址
          uri: lb://CLOUD-PROVIDER-PAYMENT # lb表示负载均衡 , 为服务实例cloud-payment-service创建动态路由
          predicates:
            - Path=/payment/get/** # 断言,路径相匹配的进行路由
        - id: payment_route2
          #          uri: http://localhost:8001
          uri: lb://CLOUD-PROVIDER-PAYMENT
          predicates:
            - Path=/payment/lb/**
  • 默认情况下Gateway会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能.
  • 需要注意的是uri的协议为lb,表示启用Gateway的负载均衡功能。
    lb://serviceName是spring cloud gateway在微服务中自动为我们创建的负载均衡uri.

此时, 我们再启动[服务提供方-8001和8002]两台, 来测试负载均衡效果.
在这里插入图片描述
访问 http://localhost:9527/payment/get/1
在这里插入图片描述
访问http://localhost:9527/payment/lb
在这里插入图片描述

6.3 断言Predicate的使用

在网关微服务的启动日志中, 可以看到加载了很多断言Predicate工厂. 这些断言Predicate工厂给路由提供了强大且灵活的配置和使用空间.
在这里插入图片描述
官网资料→
Spring Cloud Gateway作为Spring WebFlux HandlerMapping基础设施的一部分来匹配路由。Spring Cloud Gateway包括许多内置的路由谓词工厂。所有这些谓词都匹配HTTP请求的不同属性。可以使用逻辑和语句组合多个路由谓词工厂。
官网提供了以下11种断言Predicate工厂给我们使用.
在这里插入图片描述

6.3.1 After Route Predicate Factory

6.3.1.1 官网案例

after路由谓词工厂接受一个datetime参数。此谓词匹配发生在指定日期时间之后的请求。配置after路由谓词的示例如下:

spring:
  cloud:
    gateway:
      routes:
      - id: after_route`
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]
  • 2017-01-20T17:42:47.789-07:00[America/Denver] 表示美国Denver时区的时间.
  • 2021-11-17T20:51:01.655+08:00[Asia/Shanghai]表示亚洲上海时区的时间.
  • 上面断言配置, 只有在配置时间之后的请求才会被路由转发.
  • 下面的代码可以获取指定时区的时间.
@org.junit.jupiter.api.Test
public void test() {

   ZonedDateTime zonedDateTime = ZonedDateTime.now(); // 2021-11-17T20:51:01.655+08:00[Asia/Shanghai]
// zonedDateTime = ZonedDateTime.now(ZoneId.of("America/New_York")); // 2021-11-17T07:50:47.827-05:00[America/New_York]
   zonedDateTime = ZonedDateTime.now(Clock.systemDefaultZone());
   System.out.println(zonedDateTime);

}
6.3.1.2 自己案例
spring:
  application:
    name: cloud-gateway
  # 网关配置
  cloud:
    gateway:
      routes:
        - id: payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #          uri: http://localhost:8001 #匹配后提供服务的路由地址
          uri: lb://CLOUD-PROVIDER-PAYMENT # lb表示负载均衡 , 为服务实例cloud-payment-service创建动态路由
          predicates:
            - Path=/payment/get/** # 断言,路径相匹配的进行路由
        - id: payment_route2
          #          uri: http://localhost:8001
          uri: lb://CLOUD-PROVIDER-PAYMENT
          predicates:
            - After=2021-11-17T20:52:34.422+08:00[Asia/Shanghai]

6.3.2 Before Route Predicate Factory

6.3.2.1 官网案例

before路由谓词工厂接受一个datetime参数。该谓词匹配在指定日期之前发生的请求。配置before路由谓词的示例如下:

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

这条路由匹配2017年1月20日17:42之后的任何请求

6.3.2.2 自己案例
spring:
  application:
    name: cloud-gateway
  # 网关配置
  cloud:
    gateway:
      routes:
        - id: payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #          uri: http://localhost:8001 #匹配后提供服务的路由地址
          uri: lb://CLOUD-PROVIDER-PAYMENT # lb表示负载均衡 , 为服务实例cloud-payment-service创建动态路由
          predicates:
            - Path=/payment/get/** # 断言,路径相匹配的进行路由
        - id: payment_route2
          #          uri: http://localhost:8001
          uri: lb://CLOUD-PROVIDER-PAYMENT
          predicates:
            - Before=2021-11-05T15:10:03.685+08:00[Asia/Shanghai]

6.3.3 Between Route Predicate Factory

6.3.3.1 官网案例

路由谓词工厂接受两个参数,datetime1和datetime2。该谓词匹配发生在datetime1之后和datetime2之前的请求。datetime2参数必须在datetime1之后。下面的示例配置一个路由谓词:

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]

此路由匹配于2017年1月20日17:42山地时间(丹佛)之后和2017年1月21日17:42山地时间(丹佛)之前的任何请求。这对于维护窗口可能很有用。

6.3.3.2 自己案例
spring:
  application:
    name: cloud-gateway
  # 网关配置
  cloud:
    gateway:
      routes:
        - id: payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #          uri: http://localhost:8001 #匹配后提供服务的路由地址
          uri: lb://CLOUD-PROVIDER-PAYMENT # lb表示负载均衡 , 为服务实例cloud-payment-service创建动态路由
          predicates:
            - Path=/payment/get/** # 断言,路径相匹配的进行路由
        - id: payment_route2
          #          uri: http://localhost:8001
          uri: lb://CLOUD-PROVIDER-PAYMENT
          predicates:
            - Between=2021-11-17T20:52:34.422+08:00[Asia/Shanghai],2021-11-25T15:10:03.685+08:00[Asia/Shanghai]
6.3.4.1 官网案例

cookie路由谓词工厂接受两个参数,cookie名称和正则表达式。该谓词匹配具有给定名称且其值与正则表达式匹配的cookie。下面的示例配置一个cookie路由谓词工厂:

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

该路由匹配具有名为chocolate的cookie且其值与ch.p正则表达式匹配的请求。没有匹配上的请求将不会被路由转发.

6.3.4.2 自己案例
spring:
  application:
    name: cloud-gateway
  # 网关配置
  cloud:
    gateway:
      routes:
        - id: payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #          uri: http://localhost:8001 #匹配后提供服务的路由地址
          uri: lb://CLOUD-PROVIDER-PAYMENT # lb表示负载均衡 , 为服务实例cloud-payment-service创建动态路由
          predicates:
            - Path=/payment/get/** # 断言,路径相匹配的进行路由
        - id: payment_route2
          #          uri: http://localhost:8001
          uri: lb://CLOUD-PROVIDER-PAYMENT
          predicates:
            - Cookie=uname, xxx

CURL测试
不带cookie测试 , curl http://localhost:9527/payment/lb
在这里插入图片描述
带cookie测试 , curl http://localhost:9527/payment/lb –cookie “uname=xxx”
在这里插入图片描述

6.3.5 Header Route Predicate Factory

header路由谓词工厂接受两个参数,header属性名称和正则表达式。该谓词与具有指定名称且其值与正则表达式匹配的header相匹配。下面的示例配置头路由谓词:

6.3.5.1 官网案例
spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+

如果请求有一个名为X-Request-Id的头,该头的值匹配\d+正则表达式(也就是说,它有一个或多个数字的值),则该路由匹配。属性值和正则表达式匹配则执行。

6.3.5.2 自己案例
spring:
  application:
    name: cloud-gateway
  # 网关配置
  cloud:
    gateway:
      routes:
        - id: payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #          uri: http://localhost:8001 #匹配后提供服务的路由地址
          uri: lb://CLOUD-PROVIDER-PAYMENT # lb表示负载均衡 , 为服务实例cloud-payment-service创建动态路由
          predicates:
            - Path=/payment/get/** # 断言,路径相匹配的进行路由
        - id: payment_route2
          #          uri: http://localhost:8001
          uri: lb://CLOUD-PROVIDER-PAYMENT
          predicates:
            - Header=X-Request-Id, \d+  # 请求头要有X-Request-Id属性并且值为整数的正则表达式

CURL测试
curl http://localhost:9527/payment/lb -H “X-Request-Id:123” 在这里插入图片描述

6.3.6 Host Route Predicate Factory

主机路由谓词工厂接受一个参数:主机名模式列表。Ant-style模式, 以.作为分隔符。此谓词匹配匹配模式的Host头。配置主机路由谓词的示例如下:

6.3.6.1 网关案例
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org

如果请求的Host头的值为www.somehost.org或beta.somehost.org或www.anotherhost.org,则此路由匹配并执行。

6.3.6.2 自己案例
spring:
  application:
    name: cloud-gateway
  # 网关配置
  cloud:
    gateway:
      routes:
        - id: payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #          uri: http://localhost:8001 #匹配后提供服务的路由地址
          uri: lb://CLOUD-PROVIDER-PAYMENT # lb表示负载均衡 , 为服务实例cloud-payment-service创建动态路由
          predicates:
            - Path=/payment/get/** # 断言,路径相匹配的进行路由
        - id: payment_route2
          #          uri: http://localhost:8001
          uri: lb://CLOUD-PROVIDER-PAYMENT
          predicates:
            - Host=**.atguigu.com,**.baidu.com  # 接收一组匹配的域名列表, 参数中的主机地址作为匹配规则

CURL测试
curl http://localhost:9527/payment/lb -H “Host: www.atguigu.com"
在这里插入图片描述

6.3.7 Method Route Predicate Factory

方法路由谓词工厂接受一个或多个参数:要匹配的HTTP方法。下面的示例配置一个方法路由谓词:

6.3.7.1 官网案例
spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST

如果请求方法是GET或POST,则此路由匹配。

6.3.7.2 自己案例
spring:
  application:
    name: cloud-gateway
  # 网关配置
  cloud:
    gateway:
      routes:
        - id: payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #          uri: http://localhost:8001 #匹配后提供服务的路由地址
          uri: lb://CLOUD-PROVIDER-PAYMENT # lb表示负载均衡 , 为服务实例cloud-payment-service创建动态路由
          predicates:
            - Path=/payment/get/** # 断言,路径相匹配的进行路由
        - id: payment_route2
          #          uri: http://localhost:8001
          uri: lb://CLOUD-PROVIDER-PAYMENT
          predicates:
            - Method=GET

curl测试
错误请求 curl -X -POST http://localhost:9527/payment/lb
在这里插入图片描述
正确请求 curl http://localhost:9527/payment/lb
在这里插入图片描述

6.3.8 Path Route Predicate Factory

Path路由谓词工厂接受两个参数:一个Spring PathMatcher模式列表和一个名为matchOptionalTrailingSeparator的可选标志。配置路径路由谓词的示例如下:

6.3.8.1 官网案例
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment},/blue/{segment}

如果请求路径为:/red/1或/red/blue或/blue/green,则此路由匹配。

6.3.8.2 自己案例
spring:
  application:
    name: cloud-gateway
  # 网关配置
  cloud:
    gateway:
      routes:
        - id: payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #          uri: http://localhost:8001 #匹配后提供服务的路由地址
          uri: lb://CLOUD-PROVIDER-PAYMENT # lb表示负载均衡 , 为服务实例cloud-payment-service创建动态路由
          predicates:
            - Path=/payment/get/** # 断言,路径相匹配的进行路由

curl 测试在这里插入图片描述

6.3.9 Query Route Predicate Factory

Query 路由谓词工厂接受两个参数:一个必需的参数和一个可选的regexp。配置查询路由谓词的示例如下:

6.3.9.1 官网案例
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=color,red

如果请求包含color查询参数且匹配red属性值,则上述路由匹配。(支持传入两个参数,一个是属性名,一个为属性值,属性值可以是正则表达式。)

6.3.9.2 自己案例
spring:
  application:
    name: cloud-gateway
  # 网关配置
  cloud:
    gateway:
      routes:
        - id: payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #          uri: http://localhost:8001 #匹配后提供服务的路由地址
          uri: lb://CLOUD-PROVIDER-PAYMENT # lb表示负载均衡 , 为服务实例cloud-payment-service创建动态路由
          predicates:
            - Path=/payment/get/** # 断言,路径相匹配的进行路由
        - id: payment_route2
          #          uri: http://localhost:8001
          uri: lb://CLOUD-PROVIDER-PAYMENT
          predicates:
            - Query=uname, \d+  # 支持传入两个参数,一个是属性名,一个为属性值,属性值可以是正则表达式

curl测试
错误请求 curl http://localhost:9527/payment/lb?uname=-31
在这里插入图片描述
正确请求 curl http://localhost:9527/payment/lb?uname=31
在这里插入图片描述

6.3.10 RemoteAddr Route Predicate Factory

RemoteAddr路由谓词工厂接受cidr符号(IPv4或IPv6)字符串的列表(最小大小为1),例如192.168.0.1/16(其中192.168.0.1是IP地址,16是子网掩码)。下面的示例配置RemoteAddr路由谓词:

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

如果请求的远程地址是192.168.1.10,则此路由匹配.

6.3.11 Weight Route Predicate Factory

weight路由谓词工厂有两个参数:group和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

这条路线将把80%的流量转发给weighthigh.org,把20%的流量转发给weighlow.org

6.4 过滤器Filter的使用

Spring Cloud Gateway 提供了两种过滤器类型给我们使用, GateWayFilterGlobalFilter. GateWayFilter提供了很多已经实现的过滤器. 自定义全局过滤器通过实现GlobalFilter接口来完成.

6.4.1 GateWayFilter介绍

路由过滤器允许以某种方式修改传入的 HTTP 请求或传出的 HTTP 响应。 路由过滤器的范围是特定的路由。 Spring Cloud Gateway 包含许多内置的 GatewayFilter 工厂。官网资料→ 31种之多的过滤器实现.
在这里插入图片描述

6.4.2 GateWayFilter使用

在application.yml中配置使用

spring:
  application:
    name: cloud-gateway
  # 网关配置
  cloud:
    gateway:
      routes:
        - id: payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
          #          uri: http://localhost:8001 #匹配后提供服务的路由地址
          uri: lb://CLOUD-PROVIDER-PAYMENT # lb表示负载均衡 , 为服务实例cloud-payment-service创建动态路由
          predicates:
            - Path=/payment/get/** # 断言,路径相匹配的进行路由
        - id: payment_route2
          #          uri: http://localhost:8001
          uri: lb://CLOUD-PROVIDER-PAYMENT
          predicates:
            - Path=/payment/lb/**
          filters:
              #过滤器工厂会在匹配的请求头加上一对请求头,名称为X-Request-Id值为1024
            - AddRequestParameter=X-Request-Id,1024 

6.4.3 自定义全局GlobalFilter

GlobalFilter 接口与 GatewayFilter 具有相同的签名。 这些是有条件地应用于所有路由的特殊过滤器。可以用来实现全局日志记录,统一网关鉴权等功能.        官网资料→
当请求与路由匹配时,过滤 Web 处理程序会将 GlobalFilter 的所有实例和 GatewayFilter 的所有特定于路由的实例添加到过滤器链中。 这个组合过滤器链由 org.springframework.core.Ordered 接口排序,您可以通过实现 getOrder() 方法设置该接口。由于Spring Cloud Gateway区分了过滤器逻辑执行的“前置”和“后置”阶段(参见How-it-works),优先级最高的过滤器是“前置pre”阶段的第一个阶段和“后置post”阶段的最后一个阶段。
官网案例: 配置过自定义滤器链

@Bean
public GlobalFilter customFilter() {
    return new CustomGlobalFilter();
}

public class CustomGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("custom global filter");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

自己案例: 配置自定义过滤器链

package com.atguigu.springcloud.filter;

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.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Date;

/**
 * 类描述:自定义全局过滤器
 * @Author wang_qz
 * @Date 2021/11/17 22:03
 * @Version 1.0
 */
@Component
public class MyLogGateWayFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("time:" + new Date() + "\t 执行了自定义的全局过滤器: " + "MyLogGateWayFilter" + "hello");

        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        if (uname == null) {
            System.out.println("****用户名为null,无法登录");
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return response.setComplete();
        }
        return chain.filter(exchange);
    }

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

测试自定义过滤器
启动相关微服务应用后, 访问 http://localhost:9527/payment/lb , 无法正常转发.
在这里插入图片描述
带参数访问 http://localhost:9527/payment/lb?uname=xxx, 正常转发.
在这里插入图片描述


文章作者: 王子
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 王子 !
评论
  目录