Gateway网关简介以及使用

目录

    • 一、概述简介
      • 1.1. Gateway是什么
      • 1.2. Gateway能干嘛
      • 1.3. Zuul和gateway
      • 1.4. gateway三大核心概念
      • 1.5. gateway工作流程
    • 二、实战练习
      • 2.1. 入门demo
      • 2.2. 网关路由两种配置方式
      • 2.3. 配合注册中心使用
    • 三、Predicate的使用
      • 3.1. Predicate是什么?
      • 3.2. 12个Predicate使用讲解
    • 四、Filter的使用
      • 4.1. Filter是什么?
      • 4.2. 常用的GatewayFilter
      • 4.3. 自定义全局过滤器
      • 4.4. 实现指定路由跳过全局过滤器
    • 五、Http超时配置
      • 5.1. 全局超时
      • 5.2. 每条路由超时
    • 六、执行器API
    • 七、故障排除-日志级别

一、概述简介

官网:https://docs.spring.io/spring-cloud-gateway/docs/3.1.3/reference/html/

1.1. Gateway是什么

Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。

什么是网关?

网关(Gateway)又称网间连接器,网关在网络层实现服务之间网络互连。用程序的话来说就是请求转发。他就好比小区的门一样,你想进来小区,必须通过门才能进来。为什么要有这个门?为了方便管理以及安全考虑,就好比现在疫情期间,动不动小区封了,假如小区没有门,想要封小区还得费特别大的劲将小区给围住。

再回到程序当中,现在非常流行微服务系统,将整体应用拆分为了多个模块,那么问题来了,前端页面肯定会涉及到调用不同微服务的接口,一个微服务就代表着一个端口的应用,每个微服务都是独立的,那么前端在调用的时候不可能将访问地址设置成多个呀。设置成多个当然也可以,弊端就是开发过程当中我们涉及到环境(域名服务器ip)的切换,那还得改多个,因此网关为解决这个问题而生,网关可以将多个微服务访问的地址进行聚拢起来,你要访问哪个微服务的接口,直接访问网关,网关负责给你转发到对应的微服务地址上,因此前端只需要知道网关的地址服务地址就可以了。

Gateway和Nginx两个网关的区别

在学习Gateway网关的时候很容易联想到Nginx,甚至刚接触的时候会好奇很多架构图上面 画的两个网关竟然都用到了,那么他们两个到底有什么关联性?

下图当中圈出来的就是Gateway所在的位置。而Nginx属于在Gateway上层。甚至可以在外部请求的上层,因为Nginx可以用来做网关层的负载均衡,同时他还可以用来做流量入口。因为他本身就是服务器,实际开发当中都是通过Nginx来做流量入口的。

什么是流量入口?

流量入口的意思就是假如我有个html放到linux当中,那么外面的人怎么才能访问到linux当中的页面呢?Nginx可以用来解决这个问题,他本身就是一个服务器,使用过程就是开启端口然后监听请求,当请求来了,可以将请求 指向本机的任意可访问的html文件。

Gateway网关简介以及使用_第1张图片

Gateway可以称之为微服务网关(业务网关),而Nginx可以作为整个应用的流量网关,以及微服务网关的负载均衡。业务网关针对具体的业务需要提供特定的流控策略、缓存策略、鉴权认证策略等等。流量网关通常只专注于全局的Api管理策略,比如全局流量监控、日志记录、全局限流、黑白名单控制、接入请求到业务系统的负载均衡等,有点类似防火墙。Kong 就是典型的流量网关。

  • nginx是用C语言写的,自定义扩展的话,要么写C要么写lua
  • gateway是java语言的一个框架,可以在框架上进行代码的扩展与控制,例如:安全控制,统一异常处理,XXS,SQL注入等;权限控制,黑白名单,性能监控,日志打印等;

Gateway底层使用了Netty通讯框架

SpringCloud Gateway 使用的Webflux中的reactor-netty响应式编程组件。

Gateway网关简介以及使用_第2张图片

1.2. Gateway能干嘛

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

1.3. Zuul和gateway

目前在cloud官网当中新版本已经将zuul彻底移除了。Zuul1.0已经进入了维护阶段,而Gateway是SpringCloud团队研发的,是亲儿子产品,值得信赖。而且很多功能Zuul都没有用起来也非常的简单便捷。在版本选型上我们基本上不会再考虑zuul了。

Zuul 1.x 基于Servlet 2. 5使用阻塞架构它不支持任何长连接(如 WebSocket) Zuul 的设计模式和Nginx较像,每次 I/ O 操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,但是差别是Nginx 用C++ 实现,Zuul 用 Java 实现,而 JVM 本身会有第一次加载较慢的情况,使得Zuul 的性能相对较差。

Zuul 2.x理念更先进,想基于Netty非阻塞和支持长连接,但SpringCloud目前还没有整合。 Zuul 2.x的性能较 Zuul 1.x 有较大提升。在性能方面,根据官方提供的基准测试, Spring Cloud Gateway 的 RPS(每秒请求数)是Zuul 的 1. 6 倍

Spring Cloud Gateway 建立 在 Spring Framework 5、 Project Reactor 和 Spring Boot 2 之上, 使用非阻塞 API。Spring Cloud Gateway 还支持WebSocket, 并且与Spring紧密集成拥有更好的开发体验

1.4. gateway三大核心概念

  • Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
  • Predicate(断言):参考的是Java8的java.util.function.Predicate,开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由。
  • Filter(过滤):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。

Gateway网关简介以及使用_第3张图片
web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。

  • predicate就是我们的匹配条件;
  • 而filter,就可以理解为一个无所不能的拦截器,可以在执行前后来执行自己的逻辑,有了这两个元素,再加上目标uri,就可以实现一个具体的路由了

1.5. gateway工作流程

Gateway网关简介以及使用_第4张图片
客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler

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

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

核心逻辑:路由转发+执行过滤器链

二、实战练习

2.1. 入门demo

假如我有个8001端口的服务,然后他有以下两个接口,但是我又不想让别人通过8001端口访问,我想让他通过9527访问怎么办?很简单通过Gateway搭建一个网关服务即可解决该问题。

@RestController
@Slf4j
public class PaymentController {

    @Autowired
    private PaymentMapper paymentMapper;

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

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
        Payment payment = paymentMapper.selectById(id);
        log.info("*****查询结果:{}", payment);
        if (payment != null) {
            return new CommonResult(200, "查询成功, 服务端口:" + serverPort, payment);
        } else {
            return new CommonResult(444, "没有对应记录,查询ID: " + id + ",服务端口:" + serverPort, null);
        }
    }

    @GetMapping(value = "/payment/lb")
    public String getPaymentLB() {
        return serverPort;
    }
}

接下来搭建一个9527端口的Gateway入门级别的网关服务

1、以下是使用到的核心依赖,一般都会采用聚合工程,由父工程存放dependencyManagement当中的依赖,其他子模块引入使用的组件即可,单纯的练习图省劲的话,创建一个独立可运行的boot项目也可以。

关于搭建聚合我这里就尽量减省步骤了,不会搭建的可以看我的这一篇文章:https://blog.csdn.net/weixin_43888891/article/details/125267683?spm=1001.2014.3001.5502

<dependencyManagement>
    <dependencies>
        <dependency>
           <groupId>org.springframework.bootgroupId>
           <artifactId>spring-boot-dependenciesartifactId>
           <version>2.6.8version>
           <type>pomtype>
           <scope>importscope>
       dependency>
        <dependency>
           <groupId>org.springframework.cloudgroupId>
           <artifactId>spring-cloud-dependenciesartifactId>
           <version>2021.0.3version>
           <type>pomtype>
           <scope>importscope>
       dependency>
    dependencies>
dependencyManagement>

<dependencies>
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-gatewayartifactId>
    dependency>
 dependencies>   

2、添加配置

server:
  port: 9527

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

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

3、测试访问:http://localhost:9527/payment/get/1

在这里插入图片描述
Gateway网关简介以及使用_第5张图片

4、总结

通过以上示例,我们可以基本的掌握Gateway到底是干什么的,说白了就是请求转发。

如果程序引用了spring-cloud-starter-gateway,但不希望启用网关,请设置spring.cloud.gateway.enabled=false

设置完之后再想通过网关来转发请求,会直接404。

2.2. 网关路由两种配置方式

java代码配置:

@Configuration
public class GateWayConfig {
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        RouteLocatorBuilder.Builder routes = builder.routes();

        routes.route("path_route", r ->
                r.path("/payment/get/**").uri("http://localhost:8001")).build();

        return routes.build();
    }
}

配置文件配置

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

这两种可以实现同样的结果,假如两个都存在,并且Path是一样的,代码配置的要比配置文件配置的级别高,这时候会路由到代码配置的uri当中。

实际开发当中我们基本上都会用配置文件的方式来配置,很少会用java配置。

2.3. 配合注册中心使用

以上示例是直接将请求转发到了ip+端口服务上。只有配合注册中心使用,才能通过服务名称来调用完成微服务的负载均衡。

如果对注册中心不是很了解的可以去看一下我之前的文章,有Eureka、Consul、zookeeper注册中心教程,所谓的注册中心我们可以把它当做就是一个map,服务的ip+端口号(或者是域名)就是value,而服务名称当做key。然后调用者通过服务名称来获取到调用服务的域名。这样假如微服务是集群,但是他们的服务名称是一样的,这样就可以很轻松的通过服务名来获取集群的ip+端口,有了ip+端口,就剩下负载均衡轮询调用了。

这里我直接就是通过Eureka注册中心来进行演示了。正常情况下,我们可以选择将网关服务注册到注册中心,也可以不进行注册,网关服务最重要的是要具备服务发现功能,至于要不要注册上去我们可以根据自己的业务场景来决定。

1.引入依赖

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>

2.修改配置

eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      # false表示不向注册中心注册自己。
      register-with-eureka: true
      # false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          # uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由

        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          # uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
      enabled: true

通过服务名称去注册中心获取ip和地址这不用我们操心,都由框架为我们解决了,我们只需要知道要调用的服务名称即可。

Gateway网关简介以及使用_第6张图片

uri: lb://cloud-payment-service:需要注意的是uri的协议为Ib,表示启用Gateway的负载均衡功能。

3.启动类添加注解:@EnableEurekaClient(代表的是启用Eureka)
4.测试:首先将两个提供者(两个提供者是一样的服务,只不过启动了两个,称之为集群)注册到注册中心,然后由网关来进行转发。

1个是8001,另一个是8002
Gateway网关简介以及使用_第7张图片

在这里插入图片描述

在这里插入图片描述
访问两次会发现他是轮询的。

三、Predicate的使用

3.1. Predicate是什么?

启动网关服务的时候会发现有一排日志,如下所示:
Gateway网关简介以及使用_第8张图片
Route Predicate Factories是什么?

首先我们会发现一共有12个,他们每个都有自己的应用场景,我们也可以看看官网介绍:

Spring Cloud Gateway 将路由匹配为 Spring WebFluxHandlerMapping基础架构的一部分。Spring Cloud Gateway 包含许多内置的路由谓词工厂。所有这些谓词都匹配 HTTP 请求的不同属性。您可以将多个路由谓词工厂与逻辑and语句结合起来。(谓词工厂也就是我们所说的断言)

作用:如果请求与断言相匹配则进行路由,如果不匹配直接404

Gateway网关简介以及使用_第9张图片

3.2. 12个Predicate使用讲解

  1. After Route Predicate

作用:就是通过设置一个时间,然后After代表的是后的意思,也就是设置的时间 之后 是可以访问这个路由的,在这个时间之前是访问不了的,注意:这个设置的是时区时间。

Gateway网关简介以及使用_第10张图片

public static void main(String[] args) {
    ZonedDateTime zbj = ZonedDateTime.now(); // 从默认时区中的系统时钟获取当前日期时间
    System.out.println(zbj);
//        ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间
//        System.out.println(zny);
}

执行结果:

在这里插入图片描述

用法示例: 如下示例当中会发现我实际上相当于设置了两个predicates(断言),path也算是一个,After又是一个,在实际开发当中,根据自己的实际场景可以随便使用断言。

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          # uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由
            - After=2022-08-20T00:10:15.434859+08:00[Asia/Shanghai]
  1. Before Route Predicater:Before就是设置的时间之前可以访问,过了时间之后不可以访问
- Before=2020-02-05T15:10:03.685+08:00[Asia/Shanghai]
  1. Between Route Predicate:两个时间的区间是可以访问的,过了时间之后不可以访问
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
  1. Cookie Route Predicate:Cookie路由谓词工厂有两个参数,cookie和namea regexp(这是一个 Java 正则表达式)。此谓词匹配具有给定名称且其值与正则表达式匹配的 cookie。
- Cookie=username,zzyy

不带cookie访问直接404

在这里插入图片描述

带上cookie访问

在这里插入图片描述

  1. Header Route Predicater:Header路由谓词工厂有两个参数,the和headera regexp(这是一个 Java 正则表达式)。此谓词与具有给定名称且值与正则表达式匹配的标头匹配。
- Header=X-Request-Id, \d+

在这里插入图片描述

  1. Host Route Predicate:路由谓词工厂采用Host一个参数:主机名列表patterns。该模式是一种 Ant 风格的模式,.以分隔符为分隔符。
- Host=**.baidu.com

正确:curl http://localhost:9527/payment/lb -H “Host: www.baidu.com”
正确:curl http://localhost:9527/payment/lb -H “Host: java.baidu.com”
错误:curl http://localhost:9527/payment/lb -H “Host: java.baidu.net”

  1. Method Route Predicate:设置了之后只有GET请求会路由
- Method=GET
  1. Path Route Predicate:关于path的上面示例当中我们就已经用到了。
- Path=/payment/lb/**
  1. Query Route Predicate:支持传入两个参数,一个是属性名,一个为属性值,属性值可以是正则表达式。
- Query=username, \d+  # 要有参数名username并且值还要是整数才能路由

示例:http://localhost:9527/payment/lb?username=31

  1. RemoteAddr Route Predicate:路由谓词工厂采用的RemoteAddr列表(最小大小为 1)sources,它们是 CIDR 表示法(IPv4 或 IPv6)字符串,例如192.168.0.1/16(其中192.168.0.1是 IP 地址和16子网掩码)。
- RemoteAddr=192.168.1.1/24
  1. Weight Route Predicate:Weight路由谓词工厂有两个参数:和group(weight一个 int)。权重是按组计算的。以下示例配置权重路由谓词:

Gateway网关简介以及使用_第11张图片

该路由会将约 80% 的流量转发到weighthigh.org,将约 20% 的流量转发到weightlow.org

  1. XForwarded Remote Addr Route Predicate:这可以与反向代理一起使用,例如负载平衡器或 Web 应用程序防火墙,其中仅当请求来自这些反向代理使用的受信任的 IP 地址列表时才允许请求。
- XForwardedRemoteAddr=192.168.1.1/24

可以把它当做IF判断,当满足的时候才能路由到uri,否则直接报异常。

四、Filter的使用

4.1. Filter是什么?

过滤器可以在执行方法前执行方法后进行过滤,所谓的过滤就是可以在请求上加一些操作,例如匹配到路由后可以在请求上添加个请求头,或者参数等等。

Gateway过滤器分为了两种:路由过滤器 和 全局过滤器

  1. 路由过滤器:路由过滤器针对于某一个路由进行使用,其中官网给我们提供了30多种类型的路由过滤器。

Gateway网关简介以及使用_第12张图片

  1. 全局过滤器:全局的往往是我们经常会用到的,他和路由过滤器区别就是,他是针对于所有路由进行设置的过滤规则,实际开发当中很少会针对于某一个路由使用Filter,大部分都会采用全局过滤器。

Gateway网关简介以及使用_第13张图片

4.2. 常用的GatewayFilter

(1)AddRequestHeader GatewayFilter:相当于是给匹配到路由的request,添加Header

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          # uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          filters:
            - AddRequestHeader=X-Request-red, blue
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由
@GetMapping(value = "/payment/get/{id}")
public CommonResult<Payment> getPaymentById(HttpServletRequest request, @PathVariable("id") Long id) {
    String header = request.getHeader("X-Request-red");
    System.out.println(header);
    Payment payment = paymentMapper.selectById(id);
    log.info("*****查询结果:{}", payment);
    if (payment != null) {
        return new CommonResult(200, "查询成功, 服务端口:" + serverPort, payment);
    } else {
        return new CommonResult(444, "没有对应记录,查询ID: " + id + ",服务端口:" + serverPort, null);
    }
}

在这里插入图片描述

(2)The RequestRateLimiter GatewayFilter Factory:通过这个Filter就可以利用redis来完成限流

Gateway网关简介以及使用_第14张图片

通过上面示例足以明白Filter的作用,官方提供了30多种Filter,如果有针对于单个路由需要过滤的,可以上官网查看,看看哪个符合自己的使用场景直接使用即可。

注意:下面这两种filters写法是等价的!

spring:
  cloud:
    gateway:
      routes:
      - id: setstatus_route
        uri: https://example.org
        filters:
        - name: AddRequestHeader
          args:
            status: X-Request-red, blue
      - id: setstatusshortcut_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-red, blue

4.3. 自定义全局过滤器

全局过滤器到底什么时候用?

利用全局过滤器我们可以用来做统一网关鉴权,以及全局日志记录等等。

怎么用?

实现implements GlobalFilter, Ordered两个接口,然后重写两个方法即可。一个是filter方法,一个是getOrder方法。全局过滤器可以存在多个,多个的时候根据getOrder方法的返回值大小就行排序执行,数字最小的过滤器优先执行。

@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,无法登录");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        // 这个就是继续执行的意思
        return chain.filter(exchange);
    }

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

假如我请求没有写到uname的参数,这时候直接406!

Gateway网关简介以及使用_第15张图片

带上之后访问成功!

在这里插入图片描述

除了以上常用的全局过滤器外,官网还提供了很多,比如Netty全局过滤器,websocket全局过滤器,这些基本上很少会用到,真正用到的时候看官网即可。

以下示例分别显示了如何设置全局前置和后置过滤器:其实还是基于全局过滤器,只不过展现的方式不一样了,这里是直接通过bean注解来注入到容器,然后使用的是匿名类。注释掉的代码是官网给的案例!

@Configuration
public class GateWayFilter {

    @Bean
    public GlobalFilter customGlobalFilter() {
//        return (exchange, chain) -> exchange.getPrincipal()
//                .map(Principal::getName)
//                .defaultIfEmpty("Default User")
//                .map(userName -> {
//                    //adds header to proxied request
//                    exchange.getRequest().mutate().header("CUSTOM-REQUEST-HEADER", userName).build();
//                    return exchange;
//                })
//                .flatMap(chain::filter);
        return new GlobalFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                System.out.println("请求前执行,这里可以放请求前的逻辑");
                exchange.getRequest().mutate().header("CUSTOM-REQUEST-HEADER", "lisi").build();
                return chain.filter(exchange);
            }
        };
    }

    @Bean
    public GlobalFilter customGlobalPostFilter() {
//              return (exchange, chain) -> chain.filter(exchange)
//                .then(Mono.just(exchange))
//                .map(serverWebExchange -> {
//                    //adds header to response
//                    serverWebExchange.getResponse().getHeaders().set("CUSTOM-RESPONSE-HEADER",
//                            HttpStatus.OK.equals(serverWebExchange.getResponse().getStatusCode()) ? "It worked" : "It did not work");
//                    return serverWebExchange;
//                })
//                .then();

        return new GlobalFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                return chain.filter(exchange).then(Mono.just(exchange)).map(serverWebExchange -> {
                    System.out.println("请求后执行,这里是当网关拿到转发服务的请求响应后会执行");
                    //adds header to response
                    serverWebExchange.getResponse().getHeaders().set("CUSTOM-RESPONSE-HEADER",
                            HttpStatus.OK.equals(serverWebExchange.getResponse().getStatusCode()) ? "It worked" : "It did not work");
                    return serverWebExchange;
                }).then();
            }
        };
    }
}

4.4. 实现指定路由跳过全局过滤器

在Spring Cloud Gateway中GlobalFilter可以方便的全局拦截或统计,有时候希望在某些路由中可以跳过GlobalFilter,可以通过GatewayFilter与GlobalFilter组合来实现。

1.GlobalFilter全局过滤器如下

@Component //必须加,必须加,必须加
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (exchange.getAttribute(IgnoreGlobalFilterFactor.ATTRIBUTE_IGNORE_GLOBAL_FILTER) != null) {
            return chain.filter(exchange);
        }
        System.out.println("1111111111");
        // 这个就是继续执行的意思
        return chain.filter(exchange);
    }

    /**
     * 这里假如设置1的话,他会优先于IgnoreGlobalFilterFactor过滤器,
     * 我们主要要依靠IgnoreGlobalFilterFactor过滤器来进行赋值,然后全局过滤器根据赋值没赋值决定执行不执行过滤器
     * 所以一定要保证IgnoreGlobalFilterFactor要比全局拦截器执行早,那么我们全局过滤器的这个order数值就要设置的大一点
     * @return
     */
    @Override
    public int getOrder() {
        return 10;
    }
}

2.自定义GatewayFilter

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

    public static final String ATTRIBUTE_IGNORE_GLOBAL_FILTER = "@ignoreGlobalFilter";

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

    @Override
    public GatewayFilter apply(Config config) {
        return this::filter;
    }

    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        exchange.getAttributes().put(ATTRIBUTE_IGNORE_GLOBAL_FILTER, true);
        return chain.filter(exchange);
    }

	// 这个名称就是yml当中设置的名称,也就是这个过滤器会去yml当中获取,看看有在filters当中设置IgnoreGlobalFilter,如果设置了执行,执行相当于是赋值了一下,然后在全局过滤器根据是否能拿到这个值来决定是否跳过过滤器
    @Override
    public String name() {
        return "IgnoreGlobalFilter";
    }

    public static class Config {
    }
}

3.yml配置

IgnoreGlobalFilter这个就是我们自定义的,假如哪个路由要跳过直接在filters当中设置即可。

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          # uri: http://localhost:8001          #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service #匹配后提供服务的路由地址
          filters:
            - IgnoreGlobalFilter
          predicates:
            - Path=/payment/get/**         # 断言,路径相匹配的进行路由

五、Http超时配置

5.1. 全局超时

  • connect-timeout必须以毫秒为单位指定。
  • response-timeout必须指定为 java.time.Duration
spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 1000
        response-timeout: 5s

一旦路由转发的请求,在返回response超过了5s,网关服务会直接返回给客户单504!

在这里插入图片描述

假如单个路由的不想受全局超时限制可以这样做

  - id: per_route_timeouts
    uri: https://example.org
    predicates:
      - name: Path
        args:
          pattern: /delay/{timeout}
    metadata:
      response-timeout: -1

5.2. 每条路由超时

  - id: per_route_timeouts
    uri: https://example.org
    predicates:
      - name: Path
        args:
          pattern: /delay/{timeout}
    metadata:
      response-timeout: 200
      connect-timeout: 200

六、执行器API

所谓的执行器API就是端点监控。Gateway给我们提供了大量的API,我们可以通过接口的形式访问到当前网关的路由以及Filter等状态信息。

1.要引入actuator依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-actuatorartifactId>
dependency>

2.开启执行器API

management:
  endpoint:
    gateway:
      enabled: true
  endpoints:
    web:
      exposure:
        include: gateway

这样就可以通过api来直接访问了,具体有哪些API我们可以参照官网来看。

Gateway网关简介以及使用_第16张图片

七、故障排除-日志级别

logging:
  level:
    org.springframework.cloud.gateway: trace

开启之后只要是通过网关调用服务都会有如下日志!

Gateway网关简介以及使用_第17张图片

你可能感兴趣的:(SpringCloud,gateway,java,spring,cloud,网关)