Spring Cloud Gateway 网关的使用

在之前的学习中,所有的微服务接口都是对外开放的,这就意味着用户可以直接访问,为了保证对外服务的安全性,服务端实现的微服务接口都带有一定的权限校验机制,但是由于使用了微服务,就需要每一个服务都进行一个校验,当校验逻辑需要修改时,又得修改多个应用,增加了开发负担,一个解决方式就是引入 API 网关,类似整个微服务架构的门面,所有的外部客户端都需要经过它来进行调度和过滤,相当于前台,需要办理什么业务,经过前台的确认之后会引导用户去对应的服务台

作为网关,具有以下的几个核心功能:

  1. 权限控制:对用户进行权限校验,如果校验失败就进行拦截
  2. 动态路由:一切请求先经过网关,但网关不处理业务,而是根据某种规则,把请求发送到某个微服务
  3. 负载均衡:当路由的目标服务有多个时,进行负载均衡
  4. 限流:当请求流量过高时,按照网关中配置微服务能够接受的流量进行放行,避免服务压力过大

Gateway

1. Gateway 的使用

首先需要创建一个单独的 Gateway 的服务,除了引入 gateway 的依赖之外,还需要引入 nacos 和 loadbalancer 的依赖

XML 复制代码
<dependencies>
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
  </dependency>
  <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
  </dependency>
</dependencies>

然后就需要对网关进行配置:

复制代码
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:  #网关路由配置
        - id: order-service #自定义路由规则id
          uri: lb://order-service/  #目标服务地址
          predicates:  #路由条件(满足条件才会被通行)
            - Path=/order/**

来解释一下上面的配置:

由于需要用到 Nacos 的服务发现功能,所以要把 Nacos 的配置也添加上,之后就是网关的路由配置,自定义一个路由规则 id 作为唯一标识符,方便后续对该规则进行管理和引用,然后就是目标服务地址, lb 表示使用负载均衡器将请求转发到名为 order-service 的服务,并设置了路由条件,请求的路径必须以 /order/ 开头

配置好之后启动服务,此时就可以通过网关服务来获取资源了

关于多个服务和多个路由条件的配置:

复制代码
routes:  #网关路由配置
  - id: order-service #自定义路由规则id
    uri: lb://order-service/  #目标服务地址
    predicates:  #路由条件(满足条件才会被通行)
      - Path=/order/**,/feign/**
  - id: product-service
    uri: lb://product-service
    predicates:
      - Path=/product/**

2. Route Predicate Factories

在上面的配置中,使用 predicates 来配置路由条件,其中写的规则只是字符串形式,这些字符串会被 Route Predicate Factory (路由断言工厂,也称为路由谓词工厂)读取并处理,转变为路由判断条件

https://docs.spring.io/spring-cloud-gateway/reference/spring-cloudgateway/request-predicates-factories.html

也就是这 12 种基本的实现:

来演示一下 After 的使用,如果不符合条件就会不能够访问

复制代码
predicates:  #路由条件(满足条件才会被通行)
  - Path=/order/**,/feign/**
  - After=2026-01-12T18:10:21.024727900+08:00[Asia/Shanghai]

3. Gateway Filter Factories

Predicate 是决定了请求由哪一个路由处理,Filter 过滤器是在请求处理前后做一些的逻辑的处理

Filter 又分为 Pre 和 Post 两种类型:

Pre 类型过滤器:路由处理之前执行,也就是请求转发给后端服务之前,例如对请求进行限流,添加额外的请求头信息等

Post 类型过滤器:请求执行完成后,将结果返回给客户端之前执行,例如修改响应体的主体内容,对响应路径进行重写等

Spring Cloud Gateway 中内置了很多的过滤器,用于拦截和处理 web 请求,根据作用范围可以分为 GatewayFilter (作用到单个路由或者一个分组的路由上),GlobalFilter(作用到所有的路由上)

来演示一下 AddRequestParameter 用法:

接下来再访问接口,即使 id 不传值也通过过滤器处理之后就被赋值了

除了 AddRequestParameter,观望中还提供了很多其它的过滤器

AddRequestHeader GatewayFilter Factory :: Spring Cloud Gateway

RequestRateLimiter 可以对通过网关的请求进行限流操作,采用的是令牌桶的算法

关于限流的算法有以下几种:

  1. 固定窗口:将时间划分为固定大小的窗口,每个窗口有一个计数器,用于记录在该窗口内允许通过的请求数量,每通过一个请求计数器就加一,当计数器达到设定的阈值就不允许请求通过,缺点也是非常明显,比如设定 10 分钟可以通过 10000 个请求,前 9 分钟都没有请求,最后 1 分钟处理 10000 个请求也符合要求,显然是不合理的
  2. 滑动窗口:同样将时间划分为多个小的时间窗口,但不是按固定的大窗口重置计数器,而是将窗口滑动,这也就解决了固定窗口出现的问题,不过需要记录多个小窗口的信息,性能开销比较大
  3. 漏桶算法:就像一个漏斗型的水桶,当请求到达时,会先进入漏桶,如果漏桶未满,则允许请求通过;如果漏桶已满,则拒绝请求,桶以恒定的速率出水,代表处理请求的速率,无论流入的速率如何,流出的速率始终保持一致,这也就导致了不能够处理突发的大流量
  4. 令牌桶算法:在漏桶的基础上,令牌桶中会以一定的速率生成令牌,当流量小的时候,桶中就会不断积累令牌,当遇到突发的大流量,就可以直接拿着这些令牌通过,剩余的就排队等待令牌的生成,排队等待这种处理方式,还可以结合其他策略,比如当排队队列过长时,为了避免资源过度占用,可能会对部分请求进行拒绝;或者根据业务的优先级,优先处理高优先级请求等。

再来看下一个过滤器:

Retry 就是根据当前返回的状态码来进行重试,可以设置状态码和重试次数

在上面的配置中都是使用 filter 进行配置的,如果使用 Default Filters 配置的话就是对全部路由生效

来使用 default-filters 配置一下 retry,然后把状态码设置为 502

之后就重新请求了 3 次

GlobalFilter 是全局过滤器,会应用到所有的路由请求上,全局过滤器通常用于实现安全性,性能监控和日志记录等相关的全局功能

关于负载均衡,在之前的使用中就已经用到了,也就是 GlobalFilter

下面看一下 Metrics 怎么使用,首先需要引入下面的依赖

复制代码
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

然后再开启 metrics 的配置

然后再添加一下配置,用来监控详细的信息,在 /actuator 下可以查到所有监控的链接和信息

过滤器的执行顺序:

当一个项目中,既有 GatewayFilter 又有 GlobalFilter 时,请求路由后,网关会把当前项目中的 GatewayFilter 和 GlobalFilter 合并到一个 过滤器链中,并进行排序,依次执行过滤器

每一个过滤器都必须指定一个 int 类型的 order 值,默认值为 0 ,来表示过滤器的优先级,order 值越小,优先级越高,执行顺序越靠前

Filter 通过实现 Order 接口或者添加@Order注解来指定 order 值,Spring Cloud Gateway 提供的 Filter 由 Spring 指定,用户也可以自定义 Filter,如果过滤器的 order 值一样,会按照 defaultFilter > GatewayFilter > GlobalFilter 的顺序执行

4. 自定义过滤器

4.1. 自定义 GlobalFilter

全局过滤器需要实现 GlobalFilter,Ordered 接口,然后重写 filter 和 getOrder 方法

java 复制代码
@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //Pre执行逻辑
        log.info("Pre Global Filter...");
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
            //Post执行逻辑
            log.info("Post Global Filter...");
        }));
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE; //设置优先级
    }
}

关于 Mono 方法参数的说明:

之后再去发起请求,自定义的全局过滤器已经生效了

4.2. 自定义 GatewayFilter

如果需要自定义可配置的 GatewayFilter,就需要创建一个过滤器工厂,根据读取到的配置来构造对象

定义一个类用来存储从配置文件中读取到的配置信息

java 复制代码
@Data
public class CustomConfig {
    private String name;
}

创建一个过滤器工厂 CustomGatewayFilterFactory 继承 AbstractGatewayFilterFactory 类,然后再实现 Ordered 接口,并添加过滤器的逻辑

java 复制代码
@Slf4j
@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomConfig> implements Ordered {

    public CustomGatewayFilterFactory() {
        super(CustomConfig.class);
    }


    @Override
    public GatewayFilter apply(CustomConfig config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                //Pre类型
                log.info("Pre Filter,config{}",config);
                return chain.filter(exchange).then(Mono.fromRunnable(()->{
                    log.info("Post Filter,config{}",config);
                }));
            }
        };
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

配置文件中把自定义的过滤器名称添加上,然后还需要添加传入的参数,这里过滤器的名称取的是 CustomGatewayFilterFactory 除去 GatewayFilterFactory,按照规范自定义过滤器工厂需要以 GatewayFilterFactory 为后缀

之后再启动服务,自定义的 GatewayFilter 也生效了,并且在指定优先级相同的条件下,先执行 GatewayFilter 再执行 GlobalFilter

相关推荐
啥都想学的又啥都不会的研究生7 小时前
Kubernetes in action-初相识
java·docker·微服务·容器·kubernetes·etcd·kubelet
来自星星的猫教授8 小时前
spring,spring boot, spring cloud三者区别
spring boot·spring·spring cloud
Bling_9 小时前
请求参数、路径参数、查询参数、Spring MVC/FeignClient请求相关注解梳理
java·spring·spring cloud·mvc
编程一生12 小时前
微服务相比传统服务的优势
微服务·云原生·架构
亿坊电商12 小时前
PHP框架在微服务迁移中能发挥什么作用?
开发语言·微服务·php
IT小辉同学15 小时前
Docker如何更换镜像源提高拉取速度
spring cloud·docker·eureka
Angindem19 小时前
SpringClound 微服务分布式Nacos学习笔记
分布式·学习·微服务
能来帮帮蒟蒻吗1 天前
Docker安装(Ubuntu22版)
笔记·学习·spring cloud·docker·容器
细心的莽夫1 天前
SpringCloud 微服务复习笔记
java·spring boot·笔记·后端·spring·spring cloud·微服务
hoho不爱喝酒1 天前
微服务Nacos组件的介绍、安装、使用
微服务·云原生·架构