SpringCloud——Gateway新一代网关

发布于:2025-03-13 ⋅ 阅读:(13) ⋅ 点赞:(0)

一、概述

(1)是什么

  1. 官网:Spring Cloud Gateway
  2. 体系定位

(2)微服务架构中网关在哪里

(3)能干嘛

  1. 反向代理
  2. 鉴权
  3. 流量控制
  4. 熔断
  5. 日志监控

(4)总结

二、Gateway 三大核心

(1)总述官网

(2)分

2.2.1Route(路由)

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

2.2.2Predicate(断言)

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

2.2.3Filter(过滤)

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

(3)总结

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

三、Gateway 工作流程

(1)官网总结

  1. 客户端请求发送到 Spring Cloud Gateway 后,先通过 Gateway Handler Mapping 匹配路由,再交给 Gateway Web Handler。Handler 借助过滤器链,将请求发往实际服务处理业务并返回结果
  2. 过滤器分两类
    1. Pre 过滤器:在代理请求执行,用于参数校验、权限校验、流量监控、日志输出、协议转换等
    2. Post 过滤器:在代理请求执行,可修改响应内容 / 头,输出日志、监控流量等,作用关键

(2)核心逻辑

路由转发+断言判断+执行过滤器链

四、入门配置

(1)建Module

cloud-gateway9527

(2)改POM

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

    <artifactId>cloud-gateway9527</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <dependencies>
        <!--gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--服务注册发现consul discovery,网关也要注册进服务注册中心统一管控-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <!-- 指标监控健康检查的actuator,网关是响应式编程删除掉spring-boot-starter-web dependency-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

(3)写YML

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}

(4)主启动

(5)业务类

无,不写任何业务代码,网关和业务无关

(6)测试

  1. 先启动 8500 服务中心 Consul
  2. 再启动 9527 网关入驻

五、9527 网关如何做路由映射

(1)9527 网关如何做路由映射那???

5.1.1诉求

我们目前不想暴露8001端口,希望在8001真正的支付微服务外面套一层9527网关

5.1.28001 新建 PayGateWayController

5.1.3启动 8001 支付

5.1.48001 自测通过

  1. http://localhost:8001/pay/gateway/get/1
  2. http://localhost:8001/pay/gateway/info

(2)9527 网关 YML 新增配置

(3)测试 1

5.3.1启动 Consul8500 服务

5.3.2启动 8001 支付

5.3.3启动 9527 网关

5.3.4访问说明

(1)添加网关前
  1. http://localhost:8001/pay/gateway/get/1
  2. http://localhost:8001/pay/gateway/info
(2)隐真示假,映射说明

(3)添加网关后
  1. http://localhost:9527/pay/gateway/get/1
  2. http://localhost:9527/pay/gateway/info

5.3.5目前 8001 支付微服务前面添加 GateWay 成功

(4)测试 2

5.4.1启动订单微服务测试,看看是否通过网关?

(1)修改 cloud-api-commons

PayFeignApi接口

(2)修改 cloud-consumer-feign-order80

(3)网关开启
  1. 测试通过
  2. http://localhost/feign/pay/gateway/get/1
  3. http://localhost/feign/pay/gateway/info
(4)网关关闭
  1. 测试通过
  2. http://localhost/feign/pay/gateway/get/1
  3. http://localhost/feign/pay/gateway/info
(5)结论
  1. 9527 网关是否启动,毫无影响,o (π﹏π) o
  2. 目前的配置来看,网关被绕开了……

5.4.2正确做法

(1)同一家公司自己人,系统内环境,直接找微服务
(2)不同家公司有外人,系统外访问,先找网关再服务

  1. 刷新 feign 接口 jar 包
  2. 重启 80 订单微服务
  3. 有网关正常 success
  4. 无网关异常

(5)还有问题

请看看网关 9527 的 yml 配置,映射写死问题,^_^

六、GateWay 高级特性

(1)Route 以微服务名 - 动态获取服务 URI

6.1.1痛点

6.1.2是什么

6.1.3解决 uri 地址写死问题

  1. 9527 修改前 YML
  2. 9527 修改后 YML

6.1.4测试 1

  1. 重启网关 9527,80/8001 保持不变
  2. http://localhost/feign/pay/gateway/get/1

6.1.5测试 2

  1. 如果将 8001 微服务 yml 文件端口修改为 8007,照样访问
  2. 我实际启动的程序是 main8001 但是端口名改为 8007
  3. 我们依据微服务名字,匹配查找即可uri: lb://cloud-payment-service

(2)Predicate 断言 (谓词)

6.2.1是什么

  1. Spring Cloud Gateway
  2. Route Predicate Factories这个是什么东东?

6.2.2启动微服务 gateway9527,看看 IDEA 后台的输出

6.2.3整体架构概述

6.2.4常用的内置 Route Predicate

(1)配置语法总体概述
  1. 两种配置,二选一(Most examples below use the shortcut wayConfiguring Route Predicate Factories and Gateway Filter Factories :: Spring Cloud Gateway
  2. Shortcut Configuration
  3. Fully Expanded Arguments
(2)测试地址

http://localhost:9527/pay/gateway/get/1

(3)常用断言 api
  1. After Route Predicate
    package com.atguigu.test;
    
    import java.time.ZonedDateTime;
    
    public class ZonedDateTimeDemo {
        public static void main(String[] args) {
            ZonedDateTime zbj = ZonedDateTime.now();
            System.out.println(zbj);
        }
    }

  2. Before Route Predicate
  3. Between Route Predicate
  4. Cookie Route Predicate
    1. 方法1,原生命令
    2. 方法2,postman
    3. 方法3,chrome浏览器
  5. Header Route Predicate
    1. 方法1,原生命令
    2. 方法2,postman
  6. Host Route Predicate
    1. 方法1,原生命令
    2. 方法2,postman
  7. Path Route Predicate
  8. Query Route Predicate
  9. RemoteAddr route predicate
  10. Method Route Predicate配置某个请求地址,只能用 Get/Post 方法访问,方法限制
(4)上述配置小总结

6.2.5自定义断言,XXXRoutePredicateFactory 规则

(1)痛点
  1. 原有的断言配置不满足业务怎么办?
  2. 看看 AfterRoutePredicateFactory
  3. 架构概述
  4. 模板套路
    1. 要么继承 AbstractRoutePredicateFactory 抽象类
    2. 要么实现 RoutePredicateFactory 接口
    3. 开头任意取名,但是必须以 RoutePredicateFactory 后缀结尾
(2)自定义路由断言规则步骤套路
  1. 编写步骤
    1. 新建类名 XXX 需要以 RoutePredicateFactory 结尾并继承 AbstractRoutePredicateFactory 类
    2. 重写 apply 方法
    3. 新建 apply 方法所需要的静态内部类 MyRoutePredicateFactory.Config
      (补充说明:这个 Config 类就是我们的路由断言规则,重要注意:要在pom文件里添加依赖
      <!--lombok-->
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <version>1.18.28</version>
                  <scope>provided</scope>
              </dependency>

    4. 空参构造方法,内部调用 super
    5. 重写 apply 方法第二版
  2. 完整代码 V1
    package com.atguigu.cloud.mygateway;
    
    import jakarta.validation.constraints.NotEmpty;
    import lombok.Getter;
    import lombok.Setter;
    import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
    import org.springframework.stereotype.Component;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.server.ServerWebExchange;
    
    import java.util.function.Predicate;
    
    @Component
    public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {
    
        //这个Config类就是我们的路由断言规则,重要!!!
        @Validated
        public static class Config
        {
            @Setter @Getter @NotEmpty
            private String userType; //钻/金/银和yml配置的会员等级
        }
    
        @Override
        public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config)
        {
            return new Predicate<ServerWebExchange>()
            {
                @Override
                public boolean test(ServerWebExchange serverWebExchange)
                {
                    //检查request的参数里面,userType是否为指定的值,符合配置就通过
                    String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");
    
                    if (userType == null) {
                        return false;
                    }
                    //如果说参数存在,就和config的数据进行比较
                    if(userType.equalsIgnoreCase(config.getUserType())){
                        return true;
                    }
                    return false;
                }
            };
        }
    
        public MyRoutePredicateFactory() {
            super(MyRoutePredicateFactory.Config.class);
        }
    }
(3)测试 1
  1. YML
  2. 启动后???
    1. 故障现象
    2. 导致原因
    3. 解决方案
      1. 先解决问题,让我们自定义的能用
      2. Fully Expanded Arguments
      3. http://localhost:9527/pay/gateway/get/1?userType=diamond
      4. success
(4)bug 分析

缺少 shortcutFieldOrder 方法的实现,所以不支持短格式

(5)测试 2
  1. 完整代码 02(再加上下面这段
  2. YML
  3. 重启 9527 并测试

(3) Filter过滤

6.3.1概述

(1)官网

Spring Cloud Gateway

(2)一句话
  1. SpringMVC 里面的拦截器 Interceptor,Servlet 的过滤器
  2. “pre” 和 “post” 分别会在请求被执行调用和被执行调用,用来修改请求和响应信息
    能干嘛
(3)能干嘛
  1. 请求鉴权
  2. 异常处理
  3. 记录接口调用时长统计,重点,大厂面试设计题
(4)类型
  1. 全局默认过滤器 Global Filters
    1. Spring Cloud Gateway
    2. gateway 出厂默认已有的,直接用即可,主要作用于所有的路由
    3. 不需要在配置文件中配置,作用在所有的路由上,实现 GlobalFilter 接口即可
  2. 单一内置过滤器 GatewayFilter
    1. Spring Cloud Gateway
    2. 也可称为网关过滤器,主要作用于单一路由或某个路由分组
  3. 自定义过滤器

6.3.2Gateway内置的过滤器

(1)是什么
(2)只讲解常见和通用的,Not All
(3)常用的内置过滤器
  1. 请求头 (RequestHeader) 相关组
    1. The AddRequestHeader GatewayFilter Factory
      1. 指定请求头内容 ByName
      2. 8001 微服务 PayGateWayController 新增方法
      3. 9527 网关 YML 添加过滤内容
      4. 重启 9527 和 8001 并再次调用地址
    2. The RemoveRequestHeader GatewayFilter Factory
      1. 删除请求头 ByName
      2. 修改前
      3. YML
      4. 重启 9527 和 8001 并再次调用地址
      5. 修改后
    3. The SetRequestHeader GatewayFilter Factory
      1. 修改请求头 ByName
      2. 修改前 (sec-fetch-mode)
      3. YML
      4. 重启 9527 和 8001 并再次调用地址
      5. 修改后
  2. 请求参数 (RequestParameter) 相关组
    1. The AddRequestParameter GatewayFilter Factory
    2. The RemoveRequestParameter GatewayFilter Factory
    3. 上述两个合一块
      1. YML
      2. 修改 PayGateWayController
      3. 测试
  3. 回应头 (ResponseHeader) 相关组
    1. 开启配置前,按照地址 chrome 查看一下
    2. The AddResponseHeader GatewayFilter Factory
    3. The SetResponseHeader GatewayFilter Factory
    4. The RemoveResponseHeader GatewayFilter Factory
    5. 开启配置后,上面三个配置打包一块上(见上)
  4. 前缀和路径相关组
    1. The PrefixPath GatewayFilter Factory
      1. 自动添加路径前缀
      2. 之前的正确地址:http://localhost:9527/pay/gateway/filter
      3. YML
      4. Chrome 测试
    2. The SetPath GatewayFilter Factory
      1. 访问路径修改
      2. 测试
    3. The RedirectTo GatewayFilter Factory
      1. 重定向到某个页面
      2. YML
  5. 其它
    1. Default Filters
    2. 配置在此处相当于全局通用,自定义秒变 Global

6.3.3Gateway自定义过滤器

(1)自定义全局 Filter
  1. 面试题
    1. 统计接口调用耗时情况,如何落地,谈谈设计思路
    2. 通过自定义全局过滤器搞定上述需求
  2. 案例
    1. 自定义接口调用耗时统计的全局过滤器
    2. 知识出处:Spring Cloud Gateway
    3. 步骤
      1. 新建类 MyGlobalFilter 并实现 GlobalFilterOrdered 两个接口
      2. YML(注意,把- My的那个断言去掉,我这里忘记去掉了,留着它会影响访问
      3. code
    4. 测试
      1. localhost:9527/pay/gateway/info
      2. localhost:9527/pay/gateway/filter
      3. localhost:9527/pay/gateway/get/1
(2)自定义条件 Filter
  1. 自定义,单一内置过滤器 GatewayFilter
  2. 先参考 GateWay 内置出厂默认的
    1. SetStatusGatewayFilterFactory
    2. SetPathGatewayFilterFactory
    3. AddResponseHeaderGatewayFilterFactory
    4. ......
  3. 自定义网关过滤器规则步骤套路
    1. 新建类名 XXX 需要以 GatewayFilterFactory 结尾
    2. 并继承 AbstractGatewayFilterFactory 类
    3. 新建 XXXGatewayFilterFactory.Config 内部类
    4. 重写 apply 方法
    5. 重写 shortcutFieldOrder
    6. 空参构造方法,内部调用 super
    7. 完整代码
      package com.atguigu.cloud.mygateway;
      
      import lombok.Getter;
      import lombok.Setter;
      import org.springframework.cloud.gateway.filter.GatewayFilter;
      import org.springframework.cloud.gateway.filter.GatewayFilterChain;
      import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
      import org.springframework.http.HttpStatus;
      import org.springframework.http.server.reactive.ServerHttpRequest;
      import org.springframework.stereotype.Component;
      import org.springframework.web.server.ServerWebExchange;
      import reactor.core.publisher.Mono;
      
      import java.util.ArrayList;
      import java.util.List;
      
      @Component      //千万不能忘记!!!
      public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {
      
          public static class Config{
              @Getter @Setter
              private String status;
          }
      
          @Override
          public GatewayFilter apply(MyGatewayFilterFactory.Config config) {
              return new GatewayFilter(){
                  @Override
                  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                      ServerHttpRequest request = exchange.getRequest();
                      System.out.println("进入自定义网关过滤器MyGatewayFilterFactory,status====" + config.status);
                      if (request.getQueryParams().containsKey("atguigu")) {
                          return chain.filter(exchange);
                      }else {
                          exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
                          return exchange.getResponse().setComplete();
                      }
                  }
              };
          }
      
          @Override
          public List<String> shortcutFieldOrder() {
              List<String> list = new ArrayList<String>();
              list.add("status");
              return list;
          }
      
          public MyGatewayFilterFactory() {
              super(MyGatewayFilterFactory.Config.class);
          }
      }

  4. YML
  5. 测试

七、Gateway 整合阿里巴巴 Sentinel 实现容错

见后续springcloud alibaba篇章