【微服务】网关 - Gateway(上)(day7)

news2024/12/27 18:41:10

概述

引入

在前几个系列中,使用Eureka、Consul、Nacos解决了服务注册、服务发现的问题;使用SpringCloudLoadBalancer解决了负载均衡的问题;使用OpenFeign解决了远程调用的问题。

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

对于这个问题,SpringCloud开发了网关组件。网关组件是后端服务的唯一入口,所有的请求访问后端接口时,都需要先到网关,然后网关进行校验和分发。

最开始,SpringCloud整合的网关组件是Netflix公司的Zuul组件,由于该公司宣布组件进入维护状态,不再进行新特性的开发。所以SpringCloud就自研了一款网关组件来替换Zuul,新组件的名称就是SpringCloudGateway。官方出具的测试报告表明Gateway比Zuul要快许多。

功能

权限控制:作为微服务的入口,对用户进行权限校验,如果校验失败则进行拦截。

动态路由:一切请求先经过网关,网关根据某种规则,把请求转发到对应的微服务上。

负载均衡:当路由的服务实例有多个时,网关还要进行负载均衡的处理。

限流:请求流量过高时,网关还能按照配置进行限流处理,防止服务压力过大。

网关还有许多的功能,这里就不过多进行介绍。

三大核心

路由:路由是构建网关的基本模块(毕竟网关的关键作用就是将客户端发来的所有请求按照规则发送到对应的微服务模块上),路由是由ID、目标URI、一系列的断言和过滤器组成的,如果断言为真则匹配该路由。

断言:断言其实就是一系列的规则,如果请求满足规则,才会进行路由。

过滤:表示GatewayFilter的实例,使用过滤器,可以在请求被路由之前或之后对请求进行修改。

总的来说,网关的大致服务流程就是:客户端先向网关发出请求,其次网关会根据断言找到符合的请求,然后网关会去执行过滤器,最后才会发送到服务中进行访问。访问完成之后,会再次执行过滤器的操作,然后返回到客户端。

代码案例

在此案例中,我们使用上一文章中建立的商品模块和订单模块配合这篇文章中新的网关模块一起进行测试,从而验证网关的作用。

建模块

写pom依赖

网关服务也要注册到服务中心中,因此引入nacos依赖;网关服务肯定要引入自己的依赖;网关服务还能负载均衡,因此引入负载均衡的依赖。

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.wbz</groupId>
        <artifactId>spring-cloud-test</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-gateway-9527</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>

        <!--注册中心-->
        <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-gateway</artifactId>
        </dependency>

        <!--负载均衡-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

    </dependencies>

</project>

写yml文件

server:
  port: 9527

spring:
  application:
    name: cloud-gateway-9527

  cloud:
    
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        service: ${spring.application.name}

    gateway:
      routes:
        - id: cloud-consumer-order-open-feign-84
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/order/**

gateway中配置文件的简单介绍:

id,类似于MySQL的主键,没有固定的规则但是要求唯一,建议使用服务名。

uri,路由匹配之后要路由去的地址,可以使用IP+端口号的格式,如果想要负载均衡,那就使用lb://服务名的格式。

 predicates,断言,下面有一系列的规则,匹配之后才可以进行路由。

改主启动类

@SpringBootApplication
public class GatewayApplication9527 {

    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication9527.class, args);
    }

}

把下述三个模块启动之后,去Nacos中心观察状态:

在浏览器中输入URL:127.0.0.1:9527/order/query/1,只要出现如下画面,就表示网关模块搭建成功。大家还可以输入127.0.0.1:9527/product/query/1001,发现报错,原因就是因为在网关中只配置了对订单服务的配置文件,并没有配置对商品服务的配置文件。

断言

断言其实就是一系列的规则,请求在匹配规则成功之后才会去进行路由。在SpringCloudGateway中,存在RoutePredicateFactories,即路由断言工厂。我们配置文件中的规则,最终会被路由断言工厂读取并处理,转变为路由判断的条件。SpringCloudGateway提供了许多已经写好的路由断言工厂,这些断言会匹配HTTP请求的不同属性,并且多个断言可以通过and逻辑进行组合。当然也可以自定义一个自己想要的路由断言工厂。

配置语法

一共有两种配置语法,第一种是快捷配置方式,由过滤器名开头,后面跟等号,如果有参数的话使用逗号分隔;第二种是完全展开的配置方式,name表示过滤器名或参数名,args表示要配置的参数。

spring:

  cloud:

    gateway:
      routes:
        - id: cloud-consumer-order-open-feign-84
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/order/test1/**,/order/test2/{id}

        - id: cloud-provider-product-open-feign-8401
          uri: lb://cloud-provider-product-open-feign-8401
          predicates:
            - name: Path
              args:
                name[0]: /product/test1
                name[1]: /product/test2

常用断言

AfterRoutePredicate

表示设置一个时间,当真实时间超过该时间才可以进行访问。

时间的格式是使用ZoneDateTime类的格式,可以自己使用main函数来获取一个时间:

public class ZonedDateTimeTest {

    public static void main(String[] args) {
        ZonedDateTime now = ZonedDateTime.now();
        System.out.println(now);
        // 2024-10-08T08:43:57.324919400+08:00[Asia/Shanghai]
    }

}

如下两个代码,写一个配置文件和测试代码对After断言工厂进行测试,先使用上述main函数测试出一个ZoneDateTime类型的时间,然后写入配置文件中进行测试。输入URL:127.0.0.1:9527/predicate/after,出现最下面的结果就表示配置成功;然后再把时间改换成2025年,发现没有成功访问,表示after断言工厂的作用就是当前时间必须要大于配置时间,那么才能进行路由。 

spring:
  cloud:
    gateway:
      routes:
        - id: 1
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/predicate/after
            - After=2024-10-08T08:43:57.324919400+08:00[Asia/Shanghai]
/**
 * 关于断言的测试
 */

@Slf4j
@RestController
@RequestMapping("/predicate")
public class PredicateController {

    @GetMapping("/after")
    public String after() {
        log.info("进入断言测试的After断言工厂");
        ZonedDateTime zonedDateTime = ZonedDateTime.now();
        return zonedDateTime.toString();
    }

}

BeforeRoutePredicate

表示真实时间必须在配置的时间之前才可以进行访问。

如下是对Before断言工厂的测试,表示时间要在2024年10月8日之前。

/**
 * 关于断言的测试
 */

@Slf4j
@RestController
@RequestMapping("/predicate")
public class PredicateController {

    @GetMapping("/before")
    public String before() {
        log.info("进入断言测试的Before断言工厂");
        String time = ZonedDateTime.now().toString();
        log.info("当前时间:" + time);
        return time;
    }

}
spring:
  cloud:
    gateway:
      routes:
        - id: 2
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/predicate/before
            - Before=2024-10-08T09:04:31.397228500+08:00[Asia/Shanghai]

BetweenRoutePredicate

表示当前时间要在配置的两个时间之间,符合既要又要的条件。

如下是对Between断言工厂的测试,表示时间要在2024.10.8和2025.10.8之间,才能路由到接口。

/**
 * 关于断言的测试
 */

@Slf4j
@RestController
@RequestMapping("/predicate")
public class PredicateController {

    @GetMapping("/between")
    public String between() {
        log.info("进入断言测试的Between断言工厂");
        ZonedDateTime now = ZonedDateTime.now();
        log.info("当前时间:" + now.toString());
        return now.toString();
    }

}
spring:
  cloud:
    gateway:
      routes:
        - id: 3
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/predicate/between
            - Between=2024-10-08T09:04:31.397228500+08:00[Asia/Shanghai],2025-10-08T09:04:31.397228500+08:00[Asia/Shanghai]

CookieRoutePredicate

表示在请求中必须要写Cookie,并且Cookie的格式必须和配置文件中相同才行。在配置文件中,需要两个参数,一个是Cookie的名称,一个是Cookie的值,并且值可以使用正则表达式。

如下是对Cookie断言工厂的测试,表示发送的请求必须要携带Cookie,并且值为myCookie=test才可以路由到服务进行访问。

spring:
  cloud:
    gateway:
      routes:
        - id: 4
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/predicate/cookie
            - Cookie=myCookie, test
/**
 * 关于断言的测试
 */

@Slf4j
@RestController
@RequestMapping("/predicate")
public class PredicateController {

    @GetMapping("/cookie")
    public String cookie(HttpServletRequest request) {
        return request.getHeader("Cookie");
    }

}

HeaderRoutePredicate

和Cookie断言工厂类似,Header断言也需要一个属性名和一个属性值才能进行路由,并且都要放在Header头中。

 如下是对Header断言工厂的测试,表示必须在请求头中加一个username的属性,并且值为wbz,才可以路由到服务进行访问。

spring:
  cloud:
    gateway:
      routes:
        - id: 5
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/predicate/header
            - Header=username, wbz
@Slf4j
@RestController
@RequestMapping("/predicate")
public class PredicateController {

    @GetMapping("/header")
    public String header(HttpServletRequest request) {
        return request.getHeader("username");
    }

}

HostRoutePredicate

@Slf4j
@RestController
@RequestMapping("/predicate")
public class PredicateController {

    @GetMapping("/host")
    public String host(HttpServletRequest request) {
        return request.getHeader("host");
    }

}
spring:
  cloud:
    gateway:
      routes:
        - id: 6
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/predicate/host
            - Host=com.wbz.java

MethodRoutePredicate

方法断言工厂表示对请求的方法类型进行限制。

如下是对Method断言工厂的测试,当请求类型为POST或者GET时,可以进行路由,反之其他请求类型不可用。

@Slf4j
@RestController
@RequestMapping("/predicate")
public class PredicateController {

    @RequestMapping("/method")
    public String method(HttpServletRequest request) {
        return request.getMethod();
    }

}
spring:
  cloud:
    gateway:
      routes:
        - id: 7
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/predicate/method
            - Method=GET,POST

PathRoutePredicate

Path断言工厂前面一直在用,表示请求满足一定格式才能进行路由。

如下是对Path断言工厂的测试,表示当请求路径为/predicate/path时,才能进行路由。

spring:
  cloud:
    gateway:
      routes:
        - id: 8
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/predicate/path
@Slf4j
@RestController
@RequestMapping("/predicate")
public class PredicateController {

    @GetMapping("/path")
    public String path(HttpServletRequest request) {
        return request.getRequestURI();
    }

}

QueryRoutePredicate

Query路由断言工厂表示在请求中存在该参数以及对应值才能进行路由,参数是确定的,对应值可以使用正则表达式。

如下是Query断言工厂的测试,表示必须要有参数password,并且密码是wbz才能进行路由。

spring:
  cloud:
    gateway:
      routes:
        - id: 9
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/predicate/query
            - Query=password, wbz
@Slf4j
@RestController
@RequestMapping("/predicate")
public class PredicateController {

    @GetMapping("/query")
    public String query(@RequestParam String password) {
        return password;
    }

}

自定义断言

如果想天马行空就就能自定义断言出来,这显然是不现实的,但是我们最大的技能就是模仿。所以我们可以打开任意一个常用断言模仿来做。

1. 自定义断言类,该类需要以RoutePredicateFactory结尾,前面部分自定义。

2. 继承AbstractRoutePredicateFactory抽象类或者RoutePredicateFactory接口。

3. 实现一个静态内部类,类中的内容就是我们要写在配置文件中的内容。

4. 重写apply方法,当配置文件中写了断言之后,就是依靠该方法来判断是否进行路由。

5. 重写构造方法并写入super。

自定义断言工厂的名称是My,然后当请求参数中含有type时,并且对应的值为配置文件中对应的值时,就可以进行路由。

@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {

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

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


    @Validated
    public static class Config {

        private String type;

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }
    }

}

注意,该类写好之后不能使用简单方法写配置文件,所以我们需要再加入一个方法:

@Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("type");
    }

 测试代码:

如下My断言工厂的测试代码,表示当请求中带有参数名为type,值为admin时,就能进行路由。

spring:
  cloud:
    gateway:
      routes:
        - id: 10
          uri: lb://cloud-consumer-order-open-feign-84
          predicates:
            - Path=/predicate/my
            - name: My
              args:
                type: admin
@Slf4j
@RestController
@RequestMapping("/predicate")
public class PredicateController {

    @GetMapping("/my")
    public String my(@RequestParam String type) {
        return type;
    }

}

 经过自定义断言的书写之后,让我对前面的知识又有了一定的理解。也就是配置文件的短格式是上述写的没错,但是长格式呢?通过阅读源码,我发现了配置文件中对应的key值只要和源码中的对应相同才能配置成功,否则就会报错。

例如Query的完整格式如何配置,我们只需要看源码就能得出。如下图,Query需要如下两个参数,所以我们只需要在args中把key配置成源码想要的,value配置成我们想要的即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2196624.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【优选算法】(第二十八篇)

目录 K个⼀组翻转链表&#xff08;hard&#xff09; 题目解析 讲解算法原理 编写代码 两数之和&#xff08;easy&#xff09; 题目解析 讲解算法原理 编写代码 K个⼀组翻转链表&#xff08;hard&#xff09; 题目解析 1.题目链接&#xff1a;. - 力扣&#xff08;Leet…

win11 24H2怎么安装_u盘安装win11 24H2详细步骤【支持新旧机型安装】

10月1日&#xff0c;微软正式发布了Windows 11 24H2正式版。对于win11 24h2新机器安装肯定是可以的&#xff0c;对于旧电脑在硬件配置上可能无法满足Windows 11 24h2的最低系统要求&#xff0c;如果按官方要求是无法安装win11的。但是如果采用第三方pe方式安装的话&#xff0c;…

Android Studio实现安卓图书管理系统

获取源码请点击文章末尾QQ名片联系&#xff0c;源码不免费&#xff0c;尊重创作&#xff0c;尊重劳动 171安卓小说 1.开发环境 android stuido3.6 jak1.8 2.功能介绍 安卓端&#xff1a; 1.注册登录 2.图书列表 3.图书借阅 4.借阅列表 3.系统截图

VSCode使用Code Runner插件运行时,路径错误问题

1. 问题介绍 由于Code Runner插件的工作目录与文件执行目录不同&#xff0c;而导致路径错误&#xff01; 示例演示&#xff1a; 创建根目录test-dir&#xff0c;然后在里面分别创建两个目录code和data&#xff0c;分别存放Python程序read_file.py和输入数据input.txt read_fi…

PCIe配置篇(2)——如何进行配置操作(二)

一、配置机制 我们之前提到过&#xff0c;配置空间存在于PCIe设备上&#xff0c;而处理器通常无法直接执行配置读写请求&#xff0c;因为它只能生成内存和I/O请求。这意味着RC&#xff08;Root Complex&#xff09;需要将某些访问请求转换为配置请求&#xff0c;以支持配置空间…

人像抠图换背景怎么做?5款出色抠图工具让照片更加聚焦精彩

拍了一张很赞的照片&#xff0c;结果背景一团糟&#xff0c;完全抢了人像的风头&#xff1f;又或者在社交媒体上看到别人分享的图片&#xff0c;人像突出、背景清晰&#xff0c;而自己的总是差那么点意思&#xff1f; 别担心&#xff0c;现在有了人像抠图app&#xff0c;这些烦…

YOLOv10改进策略【注意力机制篇】| EMA 即插即用模块,提高远距离建模依赖(含二次创新)

一、本文介绍 本文记录的是基于EMA模块的YOLOv10目标检测改进方法研究。EMA认为跨维度交互有助于通道或空间注意力预测&#xff0c;并且解决了现有注意力机制在提取深度视觉表示时可能带来的维度缩减问题。在改进YOLOv10的过程中能够为高级特征图产生更好的像素级注意力&#…

浅谈 WMS 的应用行业_SunWMS智慧仓储物流系统

【大家好&#xff0c;我是唐Sun&#xff0c;唐Sun的唐&#xff0c;唐Sun的Sun。一站式数智工厂解决方案服务商】 仓库管理系统&#xff08;WMS&#xff09;已经成为众多行业优化运营、提高效率和竞争力的重要工具。WMS 的应用范围广泛&#xff0c;涵盖了制造业、零售业、电商、…

数据结构--堆的深度解析

目录 引言 一、基本概念 1.1堆的概念 1.2堆的存储结构 1.3堆的特点 二、 堆的基本操作 2.1初始化 2.2创建堆 2.3插入元素 2.4删除元素 2.5堆化操作 2.6堆的判空 2.7获取堆顶元素 三、堆的常见应用 1. 优先队列 2. 堆排序 3. Top-k 问题 4. 图论中的应用 四…

超享云服务器是什么意思?是免费的吗

超享云服务器是什么意思&#xff1f;超享云服务器是一种基于云计算技术&#xff0c;提供高性能、高可靠性和可扩展性的虚拟化服务器服务。它是通过虚拟化技术在物理服务器上运行&#xff0c;能够根据用户需求进行灵活的扩展和配置。虽然一些云服务提供商可能会提供免费试用期或…

Chromium 如何查找前端Browser 等对象定义在c++中的实现呢

以前端Navigator 对象为例&#xff1a; 1、直接在vscode里面搜索"Navigator" 过滤条件*.idl&#xff0c;这样可以搜到Navigator.idl对象文件。 2、打开Navigator.idl 可以看到平级目录对应的Navigator.h 和Navigator.cc定义 3、Navigator.idl会在out\Debug\gen\thir…

【bug】finalshell向远程主机拖动windows快捷方式导致卡死

finalshell向远程主机拖动windows快捷方式导致卡死 问题描述 如题&#xff0c;作死把桌面的快捷方式拖到了finalshell连接的服务器面板中&#xff0c;导致finalshell没有响应&#xff08;小概率事件&#xff0c;有时会触发&#xff09; 解决 打开任务管理器查看finalshell进…

基于SpringBoot博物馆游客预约系统【附源码】

基于SpringBoot博物馆游客预约系统 效果如下&#xff1a; 主页面 注册界面 展品信息界面 论坛交流界面 后台登陆界面 后台主界面 参观预约界面 留言板界面 研究背景 随着现代社会的快速发展和人们生活水平的提高&#xff0c;文化生活需求也在日益增加。博物馆作为传承文化、…

2024年10款好用的图纸加密软件推荐!企业CAD图纸加密推荐

随着企业信息安全意识的不断提高&#xff0c;尤其是在工业设计和制造领域&#xff0c;保护CAD图纸等核心技术资料的安全成为企业管理的重点。图纸一旦泄露&#xff0c;可能会给企业带来巨大的经济损失。因此&#xff0c;选择一款好用的图纸加密软件&#xff0c;对企业而言尤为重…

FDS-112 土壤盐分传感器 三针 自带温度补偿功能

产品概述 土壤盐分传感器是用不锈钢探针通过变送器转换成土壤盐分的模拟或数字信号&#xff0c;再经过精密温度传感器将信号补偿到 25℃&#xff0c;作为土壤盐分信号输出&#xff0c;将电导值转换成与之对应的模拟或数字信号。将这种电导率传感器埋入土壤后&#xff0c;直接测…

什么是 SAP ABAP 系统的工作进程(Work Process)

ABAP 系统架构图里,Work Process 工作进程的地位如下图所示。 ABAP 工作进程的概念和作用,同《计算机操作系统》中的同名概念有所差异。 操作系统的进程,是对正在运行中的程序的一个抽象。线程则是操作系统分配处理器时间资源的基本单元,是进程之内独立执行的一个单元。对…

ARIMA|基于自回归差分移动平均模型时间序列预测

目录 一、基本内容介绍&#xff1a; 二、实际运行效果&#xff1a; 三、原理介绍&#xff1a; 四、完整程序下载&#xff1a; 一、基本内容介绍&#xff1a; 本代码基于Matlab平台&#xff0c;通过ARIMA模型对时间序列数据进行预测。程序以通过调试&#xff0c;解压后打开…

LeetCode讲解篇之852. 山脉数组的峰顶索引

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们可以采用二分查找&#xff0c;每次查询区间中点元素与中点下一个元素比较 如果中点元素大于其下一个元素&#xff0c;则表示从中点开始向右是递减趋势&#xff0c;那峰值索引一定小于等于中点&#xff0c;我…

最新版开源问答平台网站源码 适合运营做收录排名 功能类似于百度知道,360问答,知乎等问答平台搭建

最近我们的内部客户们集中反馈&#xff0c;需要一个类似于百度知道&#xff0c;360问答&#xff0c;知乎这样的网站源码系统&#xff0c;不仅可以用于外网交流&#xff0c;也可以部署于公司内网&#xff0c;可以搞成公司内网知识问答库或者内部交流社区都是可以的&#xff0c;玩…

十大跨网文件交换系统盘点,谁才是你的最佳选择?(精选顶级推荐)

在现代企业中&#xff0c;跨网文件交换系统的需求日益增加。无论是团队协作还是客户沟通&#xff0c;安全、快速地共享文件变得至关重要。本文将盘点十大跨网文件交换系统&#xff0c;其中第一款是FileLink&#xff0c;因其卓越的性能和安全性受到广泛好评。 1. FileLink跨网文…