SpringCloud学习笔记(四)

369 阅读7分钟

7.GateWay网关

7.1. 简介

  • Spring Cloud Gateway是Spring Cloud生态系统中的网关组件,用于提供路由、过滤、限流等功能。Spring Cloud Gateway基于Spring6、Spring Boot3和Project Reactor等技术。它旨在为微服务架构提供简单有效的统一API路由管理。
  • 核心是一系列的过滤器,通过这些过滤器可以将客户端的请求转发到对应的微服务。是加在整个微服务最前沿的防火墙和代理期,隐藏微服务节点IP端口信息,加强安全保护。
  • Spring Cloud Gateway本身也是微服务,需要注册进服务注册中心。

7.2.网关定位

  • GateWay VS Nginx:Nginx位于客户端和服务器之间,用于反向代理和负载均衡,而Spring Cloud Gateway位于微服务和Nginx之间,用于路由、过滤和限流。

7.3. GateWay三大核心

  • 路由:路由是GateWay中最基础的功能,它将客户端的请求转发到对应的微服务。由ID、URI、断言、过滤器组成,如果断言为true则匹配该路由。
  • 断言:断言是GateWay中用于判断请求是否满足特定条件的机制,开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),只有满足条件的请求才会被转发到对应的微服务。
  • 过滤:过滤是GateWay中用于处理请求和响应的机制,它可以在请求被路由前或者之后对请求进行处理。

7.4. GateWay工作流程

  • 客户端向GateWay发送请求,GateWay根据请求的路径和路由规则,将请求发送到WebHandler。Handler通过指定的过滤器链来将请求发送给对应的微服务。
  • 过滤器可能会在发送代理之前或之后执行业务逻辑。
  • "pre"类型的过滤可以做参数校验、权限校验、流量监控、日志输出、协议转换等,
  • "post"类型的过滤器可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。

7.5. GateWay路由配置

  • 配置文件
server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  gateway:
    routes:
      - id: payment_route1
#          uri: http://localhost:9001
        uri: lb://nacos-pay-provider #动态路由,直接寻找微服务,支持负载均衡
        predicates:
          - Path=/pay/gateway/get/**
      - id: payment_route2
#          uri: http://localhost:9001
        uri: lb://nacos-pay-provider
        predicates:
          - Path=/pay/gateway/info/**
  • openfeign配置
@FeignClient(value = "cloud-gateway") //微服务对应网关
public interface PayFeignApi {

    @GetMapping("/pay/nacos/{id}")
    public ResultData<String> getPayInfo(@PathVariable("id") Integer id);

    @GetMapping(value="/pay/micrometer/{id}")
    public String myMicrometer(@PathVariable("id") Integer id);

    @GetMapping("/pay/gateway/get/{id}")
    public ResultData<PayDTO> getById(@PathVariable("id") Integer id);

    @GetMapping("/pay/gateway/info")
    public ResultData<String> getInfo();
}

客户端发送请求后,openfeign会寻找对应网关,成功访问9527端口下的 /pay/gateway/get/** 和 /pay/gateway/info/** ,请求会被转发到9001端口。

7.6. GateWay断言

  • Path:路径断言,用于匹配请求路径。例如,Path=/pay/gateway/get/**表示匹配所有以/pay/gateway/get/开头的请求路径。
  • After:时间断言,用于匹配请求时间。例如,After=2022-01-01T00:00:00.000+0000表示匹配在2022年1月1日00:00:00之后发出的请求。
  • Before:时间断言,用于匹配请求时间。例如,Before=2022-01-01T00:00:00.000+0000表示匹配在2022年1月1日00:00:00之前发出的请求。
  • Cookie:Cookie断言,用于匹配请求中的Cookie。例如,Cookie=username,zzyy表示匹配Cookie中包含username=zzyy的请求。
  • Header: Header断言,用于匹配请求头。例如,Header=X-Request-Id, \d+表示匹配请求头中包含X-Request-Id且其值为数字的请求。
  • Host: 匹配请求的主机名。例如,Host=**.zzyy.com表示匹配所有以.zzyy.com结尾的主机名。
  • Query: 匹配请求的查询参数。例如,Query=username, \d+表示匹配查询参数中包含username=整数的请求。例如请求为/pay/gateway/get/1?username=1,则匹配成功。
  • RemoteAddr: 匹配请求的远程地址。例如,RemoteAddr=192.168.1.1/24表示匹配来自192.168.1.0/24网段的请求。
  • Metthod: 匹配请求的方法。例如,Method=GET表示匹配GET请求。

示例代码:

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  gateway:
    routes:
      - id: payment_route1
#          uri: http://localhost:9001
        uri: lb://nacos-pay-provider #动态路由,直接寻找微服务,支持负载均衡
        predicates:
          - Path=/pay/gateway/get/**
          - After=2025-02-05T15:08:30.031606300+08:00[Asia/Shanghai]
          - Cookie=username,zzyy
          - Header=X-Request-Id, \d+
          - Host=**.zzyy.com
          - Query=username, \d+
          - RemoteAddr=192.168.83.1/24
          - Method=GET, POST
      - id: payment_route2
#          uri: http://localhost:9001
        uri: lb://nacos-pay-provider
        predicates:
          - Path=/pay/gateway/info/**

7.7. GateWay自定义断言

7.7.1. 自定义步骤
  1. 新建类名以RoutePredicateFactory结尾,继承AbstractRoutePredicateFactory
  2. 重写apply方法
  3. 新建apply方法所需要的静态内部类MyRoutePredicateConfig,这个是路由断言规则
  4. 空参构造方法,内部调用super
  5. 重写apply方法第二版
7.7.2. 代码实现
  1. 编写自定义断言类
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {


    public MyRoutePredicateFactory() {
        super(MyRoutePredicateFactory.Config.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");
                if (userType == null) {
                    return false;
                }
                if (userType.equalsIgnoreCase(config.getUserType())) {
                    return true;
                }
                return false;
            }
        };
    }

    public static class Config {
        @Setter@Getter@NotEmpty
        private String userType; //用户等级
    }
    # 支持短格式书写配置
    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("userType");
    }
}

2. 编写配置文件

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  gateway:
    routes:
      - id: payment_route1
#          uri: http://localhost:9001
        uri: lb://nacos-pay-provider #动态路由,直接寻找微服务,支持负载均衡
        predicates:
          - Path=/pay/gateway/get/**
#            - name: My
#              args:
#                userType: diamond
          - My=diamond

7.8. GateWay过滤器

7.8.1. 过滤器的作用
  • 过滤器可以在请求被路由之前或之后对请求进行修改,或者对路由的请求响应进行修改。
7.8.2. GateWay内置过滤器
  • 过滤器类型

    • GatewayFilter
    • GlobalFilter
    • 自定义Filter
  • 过滤器作用范围

    • 单个路由
    • 全局
7.8.3. GateWay常用内置过滤器

1.RequestHeader:

  • AddRequestHeader:添加请求头
  • RemoveRequestHeader:移除请求头
  • SetRequestHeader:设置请求头

2.RequestParameter:

  • AddRequestParameter:添加请求参数
  • RemoveRequestParameter:移除请求参数
  • SetRequestParameter:设置请求参数

3.ResponseHeader:

  • AddResponseHeader:添加响应头
  • RemoveResponseHeader:移除响应头
  • SetResponseHeader:设置响应头

4.Path:

  • PrefixPath:路径前缀
  • SetPath:设置替换路径
  • RedirectTo:重定向

配置文件:

spring:
  application:
    name: cloud-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      routes:
        - id: payment_route1
#          uri: http://localhost:9001
          uri: lb://nacos-pay-provider #动态路由,直接寻找微服务,支持负载均衡
          predicates:
            - Path=/pay/gateway/get/**
            - After=2025-02-05T15:08:30.031606300+08:00[Asia/Shanghai]
            - Cookie=username,zzyy
            - Header=X-Request-Id, \d+
            - Host=**.zzyy.com
            - Query=username, \d+
#            - RemoteAddr=192.168.83.1/24
            - Method=GET, POST
#            - name: My
#              args:
#                userType: diamond
            - My=diamond
        - id: payment_route2
#          uri: http://localhost:9001
          uri: lb://nacos-pay-provider
          predicates:
            - Path=/pay/gateway/info/**
        - id: payment_route3
            #          uri: http://localhost:9001
          uri: lb://nacos-pay-provider
          predicates:
            - Path=/pay/gateway/filter/**
#            - Path=/gateway/filter/**
#            - Path=/XYZ/abc/{segment}
          filters:
            - RedirectTo=302,http://www.baidu.com #302错误进行跳转
#            - SetPATH=/pay/gateway/{segment} # 上面配置的/XYZ/abc会被替换为当前路径
#            - PrefixPath= /pay # 前缀由配置统一管理
#            - AddRequestHeader=X-Request-yeffky,yeffky
#            - RemoveRequestHeader=cookie
#            - SetRequestHeader=X-Request-Id,123456
#            - AddRequestParameter=customerId,9527001
#            - RemoveRequestParameter=customerName
#            - AddResponseHeader=X-Response-Id,BlueResponse
#            - RemoveResponseHeader=Content-Type
#            - SetResponseHeader=Date,2099-11-11
7.8.4.自定义过滤器
  • 自定义全局Filter:统计接口调用耗时
@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {

    public static final String BEGIN_VISIT_TIME = "begin_visit_time";
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put(BEGIN_VISIT_TIME, System.currentTimeMillis());
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
            Long beginVisitTime = exchange.getAttribute(BEGIN_VISIT_TIME);
            if (beginVisitTime != null) {
                log.info("访问接口主机:" + exchange.getRequest().getURI().getHost());
                log.info("访问接口端口:" + exchange.getRequest().getURI().getPort());
                log.info("访问接口URL:" + exchange.getRequest().getURI().getPath());
                log.info("访问接口参数:" + exchange.getRequest().getURI().getRawQuery());
                log.info("访问接口时长:" + (System.currentTimeMillis() - beginVisitTime) + "ms");
                log.info("=================");
                System.out.println();
            }
        }));
    }

//    数字越小,优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}
  • 自定义条件Filter

    -配置类

@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> {

    public MyGatewayFilterFactory() {
        super(MyGatewayFilterFactory.Config.class);
    }


    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                ServerHttpRequest request = exchange.getRequest();
                System.out.println("进入了自定义网关过滤器:" + config.getStatus());
                if (request.getQueryParams().containsKey(config.getStatus())) {
                    return chain.filter(exchange);
                } else {
                    exchange.getResponse().setStatusCode(HttpStatus.BAD_GATEWAY);
                    return exchange.getResponse().setComplete();
                }

            }
        };
    }

    public static class Config {
        @Getter @Setter
        private String status;
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("status");
    }
}

-yml配置

spring:
  cloud:
    gateway:
      routes:
        - id: payment_route3
            #          uri: http://localhost:9001
          uri: lb://nacos-pay-provider
          predicates:
            - Path=/pay/gateway/filter/**
          filters:
            - My=yeffky #需要有yeffky这个键,而非键值对为"status: yeffky"