[Spring] Gateway详解

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343

🏵️热门专栏:

🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482

🍕 Collection与数据结构 (93平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482

🧀线程与网络(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482

🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482

🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482

🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482

🎃Redis(97平均质量分)https://blog.csdn.net/2301_80050796/category_12777129.html?spm=1001.2014.3001.5482

🐰RabbitMQ(97平均质量分) https://blog.csdn.net/2301_80050796/category_12792900.html?spm=1001.2014.3001.5482

感谢点赞与关注~~~

目录

  • [1. 网关介绍](#1. 网关介绍)
    • [1.1 问题](#1.1 问题)
    • [1.2 什么是API网关](#1.2 什么是API网关)
  • [2. Spring Cloud Gateway](#2. Spring Cloud Gateway)
    • [2.1 快速上手](#2.1 快速上手)
    • [2.2 Route Predicate Factories(路由谓词工厂)](#2.2 Route Predicate Factories(路由谓词工厂))
      • [2.2.1 Predicate](#2.2.1 Predicate)
      • [2.2.2 Route Predicate Factories](#2.2.2 Route Predicate Factories)
      • [2.2.3 代码演示](#2.2.3 代码演示)
    • [2.3 Gateway Filter Factories(网关过滤器工厂)](#2.3 Gateway Filter Factories(网关过滤器工厂))
    • [2.4 过滤器的执行顺序](#2.4 过滤器的执行顺序)
    • [2.5 自定义过滤器](#2.5 自定义过滤器)

1. 网关介绍

1.1 问题

在前面,我们通过Eureka,Nacos解决了服务注册,服务发现的问题,使用Spring Cloud LoadBalance解决了负载均衡的问题,使用OpenFeign解决了远程调用的问题.

但是当前所有微服务的接口都是直接对外暴露的,可以直接通过外部访问.为了保证对外服务的安全性,服务端实现的微服务接口通常都带有一定的权限校验机制.由于使用了微服务,原本⼀个应用的的多个模块拆分成了多个应用,我们不得不实现多次校验逻辑.当这套逻辑需要修改时,我们需要修改多个应用,加重了开发人员的负担.

针对上述的问题,一个最常用的方案就是使用API网关.

比如外部人员去一个公司办理业务,公司需要先核实对方的身份之后再去进行办理,而且公司划分为了多个部门,每个部门都需要先对对方的身份进行核实之后再进行办理.这样的效率是非常低的,

为此我们对此进行了改进,设立前台,统一又前台来进行身份的核实,前台身份校验通过之后,其他部门就可以设置信任,直接办理业务.

1.2 什么是API网关

API网关(简称网关)也是一个服务,通常是后端服务的唯一入口,它的定义类似设计模式中的门面模式.它就类似与整个微服务架构的门面,所有的外部客户访问,都要经过它来进行调度和过滤.

网关核心功能:

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

常见的网关实现也有很多种,我们今天要讲的是Spring Cloud Gateway

2. Spring Cloud Gateway

2.1 快速上手

我们通过一下的演示,先来了解网关的基本功能.

  1. 创建网关项目
    API网关也是一个服务
  2. 引入网关依赖
xml 复制代码
<dependencies>
    <!--⽹关-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!--基于nacos实现服务发现依赖-->
    <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>
  1. 编写启动类
java 复制代码
@SpringBootApplication
public class GateWayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GateWayApplication.class,args);
    }
}
  1. 添加Gateway的路由设置
    创建application.yml文件,添加如下的配置:
yml 复制代码
server:
  port: 10030
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
        - id: product-service
          uri: lb://product-service
          predicates:
            - Path=/product/**
        - id: order-service
          uri: lb//order-service
          predicates:
            - Path=/order/**

配置解释:

  • id: 自定义路由ID,保持唯一即可.
  • uri: 目标服务地址,支持普通URI以及以及lb://应用注册服务名称.lb表示负载均衡,使用该格式表示从注册中心获取服务地址.
  • predicates: 路由条件,根据匹配的结果决定是否执行该请求路由,上述代码中,我们把符合Path规则的一切请求,都代理到uri参数指定的地址.
  1. 测试
    启动API网关服务
    通过网关服务访问product-service
    http://127.0.0.1:10030/order/select?id=1

    url符合yml文件中配置的 /product/** 规则
    通过网关访问服务order-service

    url符合yml配置文件中的/order/**规则.

2.2 Route Predicate Factories(路由谓词工厂)

2.2.1 Predicate

Predicate是java8提供一个函数式编程接口,它接受一个参数并返回一个布尔值,用于条件过滤,请求参数的校验 .

代码演示:

  1. 定义一个Predicate
java 复制代码
public class StringPredicate implements Predicate<String> {
    @Override
    public boolean test(String s) {
        return s.isEmpty();
    }
}
  1. 使用这个Predicate
java 复制代码
public static void main(String[] args) {
    Predicate<String> predicate = new StringPredicate();
    System.out.println(predicate.test(""));
    System.out.println(predicate.test("aaaa"));
}
  1. Predicate的其他写法

可以使用匿名内部类的写法

java 复制代码
public static void main(String[] args) {
    Predicate<String> predicate = new Predicate<String>() {
        @Override
        public boolean test(String s) {
            return s.isEmpty();
        }
    };
    System.out.println(predicate.test(""));
    System.out.println(predicate.test("aaa"));
}

lambda写法:

java 复制代码
public static void main(String[] args) {
    Predicate<String> predicate = s -> s.isEmpty();
    System.out.println(predicate.test(""));
    System.out.println(predicate.test("aaa"));
}
  1. Predicate的其他方法

2.2.2 Route Predicate Factories

Route Predicate Factories (路由断言工厂,也称为路由谓词工厂,此处谓词表示一个函数),在SpringCloud Gateway中,Predicate提供了路由规则的匹配机制 .

我们在配置文件中写的断言规则只是字符串,这些字符串会被Route Predicate Factory读取并处理,转变为路由判断条件.

比如前面章节配置的Path=/product/**就是通过Path属性来匹配URL前缀是/product的请求.

这个规则是由org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory来实现的.

SpringCloud Gateway默认提供了很多Route Predicate Factory,这些Predicate会分别匹配HTTP请求不同的属性,并且多个Predicate可以通过and逻辑进行组合.


2.2.3 代码演示

  1. 添加Predicate规则
    在application.yml中添加如下的规则
    在application中添加如下规则
yml 复制代码
server:
  port: 10030
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
        - id: product-service
          uri: lb://product-service
          predicates:
            - Path=/product/**
            - After=2025-01-30T00:00:00.000+08:00[Asia/Shanghai]
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
            - After=2025-01-30T00:00:00.000+08:00[Asia/Shanghai]

测试:http://127.0.0.1:10030/product/select?id=1001

返回了404.把时间改为2025-1-22之后再试.可以正常获取到请求.

2.3 Gateway Filter Factories(网关过滤器工厂)

Predicate决定了请求由哪一个路由处理,如果在请求之外处理前后需要加上一些逻辑,这就是Filter的作用范围了.

Filter分为两种类型: Pre类型和Post类型.
Pre类型过滤器 : 路由处理之前执行(请求转发到后端服务之前),在Pre类型过滤器中可以做鉴权,限流等.
Post类型过滤器: 请求之心完成之后,将结果返回给客户端之前执行.

SpringCloud Gateway中内置了很多Filter,用于拦截和链式处理web请求,比如权限校验,访问超时等设定.

SpringCloud Gateway从作用范围上,把Filter可分为GatewayFilter和GlobalFilter.
GatewayFilter : 应用到单个路由或者一个分组路由上.
GlobalFilter : 应用到所有路由上,也就是对所有的路由生效.

2.3.1 GatewayFilter

GatewayFilter同Predicate类似,都是在配置文件application.yml中配置.每个过滤器的逻辑都是固定的.比如配置文件中写AddRequestParameter,就可以为所有的请求添加一个参数,我们通过一个例子来演示GatewayFilter该如何使用.

快速上手
  1. 在application文件中添加过滤器.
yml 复制代码
server:
  port: 10030
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
        - id: product-service
          uri: lb://product-service
          predicates:
            - Path=/product/**
            - After=2025-01-22T00:00:00.000+08:00[Asia/Shanghai]
          filters:
            - AddRequestParameter=name,zhangsan
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
            - After=2025-01-22T00:00:00.000+08:00[Asia/Shanghai]

该filter值添加在了product-service路由下,因此只对product-service路由生效,也就是/product/**生效.

  1. 接收参数并打印

在product-service服务中接收请求的参数,并打印出来.

java 复制代码
@RequestMapping("/getParam")
public String getParam(Integer id,String name){
    System.out.println(id);
    System.out.println(name);
    return "接收参数成功";
}
  1. 测试
    发送http://127.0.0.1:10030/product/getParam?id=3

    观察控制台日志:
常见的GatewayFilter类型

SpringCloud Gateway提供了非常多的Filter.下面列出一些常见过滤器的说明:


具体可参考Spring官方文档:
https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/gatewayfilter-factories.html

Default Filters

前面的filter添加在指定路由下,所以只对当前路由生效,若需要对全部路由生效,可以使用spring.cloud.gateway.default-filters这个属性.和上面指定路由下一样,下面都是一个filter的列表.

举例:

yml 复制代码
spring:
  cloud:
    gateway:
      default-filters:
        - AddResponseHeader=X-Response-Default-Red, Default-Blue
        - PrefixPath=/httpbin

2.3.2 GlobalFilter

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

Spring Cloud Gateway内置的全局过滤器也有很多,比如:

  • Gateway Metric Filter:网关指标,提供监控指标
  • Forward Routing Filter: 用于本地forword,请求不转发到下游服务器.
  • LoadBalancer Client Filter:针对下游服务,实现负载均衡.

更多过滤器参考官方文档:https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/global-filters.html

快速上手
  1. 添加依赖
xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. 添加配置
yml 复制代码
spring:
  cloud:
    gateway:
      metrics:
        enabled: true
management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: always
    shutdown:
      enabled: true

解释上面的配置信息:

  • spring.cloud.gateway.metrics.enabled: metrics表示指标相关的配置项,enabled 是一个布尔类型的配置参数,当设置为 true 时,意味着开启Spring Cloud Gateway的指标收集功能.开启指标收集功能后,Spring Cloud Gateway会收集一系列与网关运行相关的指标数据.这些指标数据能够帮助开发者和运维人员更好地了解网关的运行状态,性能表现以及流量情况等.
  • management.endpoints.web.exposure.include: "*":该配置用于指定哪些端点(endpoints)可以通过 Web 暴露出去.* 表示所有端点都将被暴露.端点是 Spring Boot Actuator提供的用于监控和管理应用程序的接口,比如健康检查、指标统计、环境变量查看等功能都通过这些端点来实现.
  • management.endpoint.health.show-details: always:此配置针对健康检查端点(health),设置为 always 表示在健康检查响应中始终显示详细信息.
  • management.endpoint.shutdown.enabled: true: 启用了应用程序的关闭端点(shutdown).这允许通过向特定的端点发送请求来优雅地关闭应用程序.
  1. 测试
    重启网关,访问http://127.0.0.1:10030/actuator,显示所有监控的信息链接.

2.4 过滤器的执行顺序

一个项目中,即有GatewayFilter,又有GlobalFilter时,执行的先后顺序是什么呢?

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

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

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

2.5 自定义过滤器

SpringCloud Gateway提供了过滤器的扩展功能,开发者可以根据实际业务来自定义过滤器,同样自定义过滤器也支持GatewayFilter和GlobalFilter两种.

2.5.1 自定义Gateway Filter

自定义Gateway Filter,需要去实现对应的接口GatewayFilterFactory,SpringBoot默认帮我们实现的抽象类是AbstractGatewayFilterFactory,我们可以直接使用.

定义GatewayFilter

首先针对Filter的配置,使用CustomConfig来定义.

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

继承AbstractGatewayFilterFactory抽象类并重写抽象方法,实现Ordered接口用于指定优先级.

java 复制代码
@Component
@Slf4j
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomConfig> implements Ordered {
    /**
     * 执行优先级,LOWEST_PRECEDENCE表示的是最低优先级
     * @return 返回最低优先级
     */
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }

    @Override
    public GatewayFilter apply(CustomConfig config) {
        return new GatewayFilter() {
            /**
             * Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChainchain)
             * ServerWebExchange: HTTP请求-响应交互的契约, 提供对HTTP请求和响应的访问, 服
             务器端请求属性, 请求实例,响应实例等, 类似Context角色
             * GatewayFilterChain: 过滤器链
             * Mono: Reactor核心类, 数据流发布者, Mono最多只触发⼀个事件, 所以可以把
             Mono 用于在异步任务完成时发出通知.
             * Mono.fromRunnable: 创建⼀个包含Runnable元素的数据流.类似与创建了一个线程
             */
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                log.info("[Pre] Filter Request:"+config.getName());
                return chain.filter(exchange).then(Mono.fromRunnable(()-> log.info("[Post] Response Filter")));//执行请求
            }
        };
    }
}

代码说明:

  • 自定义过滤器类名统一以GatewayFilterFactory结尾,因为在默认情况下,过滤器的name会采用该定义类的前缀,这里的name=Custom(yml配置中使用).
  • apply方法中,同时包含了Pre和Postman过滤,then方法中是请求执行结束之后处理的.
  • CustomConfig是一个配置类,该类只有一个属性name,和yml的配置对应.
  • 该类需要交给Spring进行管理,所以需要加@Comopent注解.
  • getOrder表示该过滤器的优先级.
配置过滤器
yml 复制代码
spring:
  cloud:
    gateway:
      metrics:
        enabled: true
      routes:
        - id: product-service
          uri: lb://product-service
          predicates:
            - Path=/product/**
          filters:
            - name: Custom
              args:
                name: custom filter

2.5.2 自定义GlobalFilter

GlobalFilter的实现比较简单,它不需要额外的配置,只需要实现GlobalFilter接口,自动会过滤掉所有的Filter.

自定义GlobalFilter
java 复制代码
@Component
@Slf4j
public class CustomGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("[Pre] CustomGlobalFilter enter...");
        return chain.filter(exchange).then(Mono.fromRunnable(()-> log.info("[Post] CustomGlobalFilter return...")));//执行请求
    }
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}
相关推荐
组合缺一1 小时前
Solon Cloud Gateway 开发:Helloword
java·gateway·solon
奕辰杰4 小时前
关于使用微服务的注意要点总结
java·微服务·架构
m0_748230215 小时前
适用于IntelliJ IDEA 2024.1.2部署Tomcat的完整方法,以及笔者踩的坑,避免高血压,保姆级教程
java·tomcat·intellij-idea
六毛的毛5 小时前
java后端之登录认证
java·开发语言·python
customer087 小时前
【开源免费】基于SpringBoot+Vue.JS校园失物招领系统(JAVA毕业设计)
java·vue.js·spring boot·后端·开源
中國移动丶移不动7 小时前
Java 反射与动态代理:实践中的应用与陷阱
java·spring boot·后端·spring·mybatis·hibernate
DEARM LINER8 小时前
RabbitMQ 死信队列
java·rabbitmq·java-rabbitmq
组合缺一8 小时前
drools 规则引擎和 solon-flow 哪个好?solon-flow 简明教程
java·solon·flow·drools
曲奇是块小饼干_8 小时前
leetcode刷题记录(九十)——74. 搜索二维矩阵
java·数据结构·算法·leetcode·职场和发展·矩阵
码农小灰9 小时前
Spring MVC 中的 DispatcherServlet:工作流程与应用场景解析
java·spring·mvc