【SpringBoot系列】Spring cloud Gateway 动态路由到底有多简单

2023-09-20 16:26:59

🤵‍♂️ 个人主页:@香菜的个人主页,加 ischongxin ,备注csdn

✍🏻作者简介:csdn 认证博客专家,游戏开发领域优质创作者,华为云享专家,2021年度华为云年度十佳博主

🐋 希望大家多多支持,我们一起进步!😄

如果文章对你有帮助的话,

欢迎评论 💬点赞👍🏻 收藏 📂加关注+

系列文章:Spring Boot学习大纲,可以留言自己想了解的技术点

 

目录

1、概念解析

2、hello word

2.1 加入spring cloud gateway依赖

2.2 配置路由

2.3 测试

3、自定义filter

3.1 filter的分类

3.2 实现局部过滤器

3.3 自定义全局过滤器

4、自定义router

4.1 原理

4.1.1RouteDefinition 和Route的区别

4.1.2 路由的加载

4.2 实现自定义路由

4.3 Gateway的执行流程

5、内置filter 和Predicate

6、一些点

7、总结


1、概念解析

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/ 

Spring Cloud Gateway

1️⃣ 路由(route):路由是网关最基础的部分,路由信息由一个ID,一个目的URL、一组断言工厂和一 组Filter组成。如果断言为真,则说明请求URL和配置的路由匹配。

2️⃣ 断言(Predicate):Java8中的断言函数,Spring Cloud Gateway中的断言函数输入类型是 Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配 来自http Request中的任何信息,比如请求头和参数等。

3️⃣ 过滤器(Filter):一个标准的Spring WebFilter,Spring Cloud Gateway中的Filter分为两种类型: Gateway Filter和Global Filter。过滤器Filter可以对请求和响应进行处理。

2、hello word

2.1 加入spring cloud gateway依赖

创建springboot项目,加入spring cloud gateway 依赖

pom文件如下,注:有用的没几行,就是加入gateway的依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.xin</groupId>
    <artifactId>gateway-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>gateway-test</name>
    <description>gateway-test</description>
    <properties>
        <java.version>17</java.version>
        <spring-cloud.version>2022.0.4</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2.2 配置路由

不喜欢properties的配置,结构不明显,所以一般使用 application.yml

配置如下

spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: http://192.168.3.8:29001
          predicates:
            - Path=/road/**
          filters:
            - StripPrefix=1

这里加入了一个路由的配置:

路由的服务是 http://192.168.3.8:29001

路由的逻辑是路径中以road开始的路径

过滤器是StripPrefix ,会去除路径中的第一个,也就是road ,最终的实际路径为 uir+ path后面的***

2.3 测试

在浏览器中输入

http://localhost:8080/road/magic/web/index.html 可以看到结果

3、自定义filter

3.1 filter的分类

Gateway分为全局过滤器GlobalFilter和局部过滤器GatewayFilter,局部过滤器只有在路由配置中针对路由ID配置了才会使用,对应配置中的

filters属性。

3.2 实现局部过滤器

局部过滤器相当于对于每个路由会有一个实例,Gateway路由过滤器通过GatewayFilterFactory这个工厂类生成。

注意点:

1、在配置时会去掉FilterFactory 比如 StripPrefixGatewayFilterFactory 配置时使用StripPrefixGatewayFilterFactory

2、config接收配置的参数

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

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

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String token = exchange.getRequest().getHeaders().getFirst("username");

            // 是否登录成功状态
            if (token != null) {
                // 合法
                // 将用户id作为参数传递下去
                return chain.filter(exchange);
            }

            //不合法(响应未登录的异常)
            ServerHttpResponse response = exchange.getResponse();
            //设置headers
            HttpHeaders httpHeaders = response.getHeaders();
            httpHeaders.add("Content-Type", "application/json; charset=UTF-8");
            httpHeaders.add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
            //设置body
            String warningStr = "未登录或登录超时";
            DataBuffer bodyDataBuffer = response.bufferFactory().wrap(warningStr.getBytes());

            return response.writeWith(Mono.just(bodyDataBuffer));
        };
    }
    static class Config {}
}

3.3 自定义全局过滤器

全局过滤器是所有的请求都会经过,全局只有一个实例,需要实现GlobalFilter和 Ordered 接口

看下一个黑名单的实现。

@Component
@Slf4j
//  TODO 改为动态判断
public class BlackFilter implements GlobalFilter, Ordered {
    private  static final List<String> blackList=new ArrayList<>();
    static {
        blackList.add("0:0:0:0:0:0:0:1");//模拟本机ip地址
    }
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response =exchange.getResponse();
        String clientIp = request.getRemoteAddress().getHostString();
        if (blackList.contains(clientIp)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            log.error(clientIp+"在黑名单中,拒绝访问");
            String data = "request be denied";
            DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
            return response.writeWith(Mono.just(wrap));
        }
        return chain.filter(exchange);
    }

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

4、自定义router

4.1 原理

4.1.1RouteDefinition 和Route的区别

RouteDefinition 是对配置的读取的对象,纯字符串配置

Route 是将定义实例化之后的对象,比如RouteDefinition 的filter 和 Predicate的实例化对象

4.1.2 路由的加载

先看下类图

RouteLocator 接口用于获取路由信息,其有三个实现类

  • RouteDefinitionRouteLocator
  • CompositeRouteLocator
  • CachingRouteLocator

最终使用的是CachingRouteLocator,它包装了CompositeRouteLocator,而CompositeRouteLocator则组合了所有的RouteDefinitionRouteLocator。

RouteDefinitionRouteLocator 与 RouteDefinitionLocator比较容易混淆,前者是一个RouteLocator(路由定位器),后者是一个RouteDefinitionLocator(路由定义定位器),前者的 RouteDefinitionRouteLocator 主要从后者获取路由定义信息。

4.2 实现自定义路由

从原理中可以看到,我们只要实现自己的RouteDefinitionRouteLocator 就可以了 ,Gateway会自动加载路由

下面是项目中实现的从数据库中加载路由的代码

@Component
public class MysqlRouteLocator implements RouteDefinitionLocator {
    @Resource
    GatewayService gatewayService;
    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        List<RouteDefinition> definitionList = new ArrayList<>();
        try {
            definitionList = gatewayService.loadRouteDefinition();
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
        return Flux.fromIterable(definitionList);
    }
}

4.3 Gateway的执行流程

Gateway的执行流程如下:

1:Gateway的客户端回向Spring Cloud Gateway发起请求,请求首先会被HttpWebHandlerAdapter进行提取组装成网关的上下文,然后网关的上下文会传递到DispatcherHandler。

2:DispatcherHandler是所有请求的分发处理器,DispatcherHandler主要负责分发请求对应的处理器,比如将请求分发到对应RoutePredicateHandlerMapping(路由断言处理器映射器)。

3:路由断言处理映射器主要用于路由的查找,以及找到路由后返回对应的FilteringWebHandler。

4:FilteringWebHandler主要负责组装Filter链表并调用Filter执行一系列Filter处理,然后把请求转到后端对应的代理服务处理,处理完毕后,将Response返回到Gateway客户端。
在Filter链中,通过虚线分割Filter的原因是,过滤器可以在转发请求之前处理或者接收到被代理服务的返回结果之后处理。所有的Pre类型的Filter执行完毕之后,才会转发请求到被代理的服务处理。被代理的服务把所有请求完毕之后,才会执行Post类型的过滤器。
 

5、内置filter 和Predicate

6、一些点

1、RefreshRoutesEvent  发布此事件会将各个routerDefinitionLocator加载的路由放入cache中

2、可以使用Nacos或者其他的服务发现

3、在配置中使用lb://service_name 可以启用负载均衡

7、总结

spring cloud gateway 使用了webflux ,在编程的时候还是有一些不适应,但是好在gateway提供了足够多的Predicate和Filter

在日常的使用中足够了,但是定制还是蛮费劲的,不适应reactor编程方式。

更多推荐

腾讯云 Cloud Studio 实战训练:快速构建React完成H5页面还原

0️⃣前言腾讯云CloudStudio是一款在线开发工具(云IDE),它能帮助用户减少安装IDE的成本,提供一站式的在线代码开发、编译、运行和存储服务。1️⃣介绍1.项目介绍我们经常会遇到远程办公的场景,下面我们打算用云IDECloudStudio快速搭建,并开发还原一个移动端ReactH5的简版点餐系统页面,从0到1

【腾讯云Cloud Studio实战训练营】构建基于React的实时聊天应用

关于腾讯云CloudStudio构建基于CloudStudio的聊天应用(项目实战)1.注册并登录CloudStudio2.配置Git环境2.1复制SSH公钥2.2添加SSH公钥至GIt平台3.创建项目4.项目开发4.1安装依赖4.2集成tailwindcss4.3编写代码4.4项目运行示例项目完整代码及CloudSt

将 Pandas 换为交互式表格的 Python 库

Pandas是我们日常处理表格数据最常用的包,但是对于数据分析来说,Pandas的DataFrame还不够直观,所以今天我们将介绍4个Python包,可以将Pandas的DataFrame转换交互式表格,让我们可以直接在上面进行数据分析的操作。PivottablejsPivottablejs是一个通过IPythonwi

【Zabbix监控一】zabbix的原理与安装

利用一个优秀的监控软件,我们可以:●通过一个友好的界面进行浏览整个网站所有的服务器状态●可以在Web前端方便的查看监控数据●可以回溯寻找事故发生时系统的问题和报警情况总结:zabbix主要功能监控,cpu负载,内存使用,硬盘使用,网络状态,端口监视,日志监视,插件开发自定义zabbixserver端口号:10500za

【系统架构】分布式系统架构设计

1分布式系统是什么分布式系统是指由多个计算机节点组成的一个系统,这些节点通过网络互相连接,并协同工作完成某个任务。与单个计算机相比,分布式系统具有更高的可扩展性、可靠性和性能等优势,因此广泛应用于大规模数据处理、高并发访问、分布式存储等领域。分布式系统的设计目标是将计算机资源、数据和控制权分布在多个节点上,以提高系统的

使用GGML和LangChain在CPU上运行量化的llama2

MetaAI在本周二发布了最新一代开源大模型Llama2。对比于今年2月发布的Llama1,训练所用的token翻了一倍,已经达到了2万亿,对于使用大模型最重要的上下文长度限制,Llama2也翻了一倍。在本文,我们将紧跟趋势介绍如何在本地CPU推理上运行量化版本的开源Llama2。量化快速入门我们首先简单介绍一下量化的

决策树的划分依据之:信息增益率

在上面的介绍中,我们有意忽略了"编号"这一列.若把"编号"也作为一个候选划分属性,则根据信息增益公式可计算出它的信息增益为0.9182,远大于其他候选划分属性。计算每个属性的信息熵过程中,我们发现,该属性的值为0,也就是其信息增益为0.9182.但是很明显这么分类,最后出现的结果不具有泛化效果.无法对新样本进行有效预测

网络安全(黑客)自学

前言1.不要试图以编程为基础的学习开始学习我在之前的回答中,我都一再强调不要以编程为基础再开始学习网络安全,一般来说,学习编程不但学习周期长,而且实际向安全过渡后可用到的关键知识并不多一般人如果想要把编程学好再开始学习网络安全往往需要花费很长时间,容易半途而废。而且学习编程只是工具不是目的,我们的目标不是成为程序员。建

重新理解 RocketMQ Commit Log 存储协议

最近突然感觉:很多软件、硬件在设计上是有rootreason的,不是bydesgin如此,而是解决了那时、那个场景的那个需求。一旦了解后,就会感觉在和设计者对话,了解他们的思路,学习他们的方法,思维同屏:活到老学到老。问题思考1、ConsumerQueueOffset是连续的吗,为什么?2、CommitLogOffse

Rust 数据类型 之 结构体(Struct)

目录结构体(Struct)定义与声明结构体定义结构体实例结构体分类单元结构体(UnitStruct)元组结构体(TupleStruct)具名结构体(NamedStruct)结构体嵌套结构体方法例1:结构体转换为字符串描述例2:矩形的周长和面积例3:结构体字段的更新与输出关联函数结构体方法与关联函数的区别参数传递方式的区

爬虫工作者必备:使用爬虫IP轻松获得最强辅助

目录一、爬虫IP的作用与优势二、选择合适的爬虫IP服务商三、使用爬虫IP的注意事项和技巧代码示例四、合法合规使用爬虫IP总结随着互联网的发展,数据已经成为企业竞争的核心资源。而获取这些数据的有效方式,就是通过爬虫技术。但是,爬虫在运行过程中很可能会触及到目标网站的限制,从而被禁止访问甚至封号。为了解决这个问题,我们可以

热文推荐