【SpringCloud】之网关应用(进阶使用)

  🎉🎉欢迎来到我的CSDN主页!🎉🎉

🏅我是君易--鑨,一个在CSDN分享笔记的博主。📚📚

🌟推荐给大家我的博客专栏《SpringCloud开发之网关应用》。🎯🎯

🎁如果感觉还不错的话请给我关注加三连吧!🎁🎁


前言

        在上一期的博客分享中我们一起了解到了SpringCloud的配置中心的相关知识的学习以及应用的方式,本期的博客分享给大家带来的是SpringCloud的网关应用。

一、什么是网关

1. 基本概述 

         Spring Cloud Gateway是Spring官方基于Spring5.0SpringBoot2.0Project Reactor等技术开发的网关旨在为微服务框架提供一种简单而有效的统一的API路由管理方式,统一访问接口。

        Spring Cloud Gateway作为Spring Cloud生态体系中的网关,目标是替代NetflixZuul,其不仅提供统 一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全、监控/埋点和限流等等。 它是基于Netty的响应式开发模式。

2. 图解

3. 组成结构

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 可以对请求和响应进行处理。

4. 应用场景

二、网关的集成使用

1. 新建一个模块作为网关

        我们在主项目下的创建一个SpringCloud的项目作为网关模块。

       创建完成之后我们将网关模块的pom文件中自带的pom文件依赖给去除掉,包括跳过编译也去除掉。 

2.  pom文件设置

        我们首先在创建好的pom文件中先集成我们主项目的pom文件,导入网关运行服务器的依赖和网关的依赖,但是注意主项目中有没有定义web模块,否则会发生冲突。

<!--    集成主项目-->
    <parent>
        <groupId>com.yx</groupId>
        <artifactId>cloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <dependencies>
        <!--    导入网关服务器的依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
<!--        导入网关的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    </dependencies>

3. 新建配置文件进行配置

        我们在网关的项目中在main的目录下新建一个resource文件夹,在其文件夹下新建一个application.yml文件进行相应的配置

server:
  port: 8082
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true

4. 启动类标记使用nacos

        我们记得在网关启动类上的标记启用nacos的注解,启用其nacos

5. 使用网关的方式

5.1 方式一:

1. 对其yml文件配置

        在网关的yml文件中配置下述内容

spring:
  application:
#    名称
    name: gateway
  cloud:
    nacos:
      discovery:
#        地址
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true

 2. 测试演示

        我们在我们要访问的请求方法中编写一个输入语句便于测试

         我们在网页进行测试

         有上述的动图所示,我们先是访问使用生成者的接口访问其请求方法;然后我们在使用网关的接口访问其生产者的请求接口,该生产者控制台输出对应的输出语句。

 5.2 方式二:

 1. 对其yml文件进行配置

        我们将之前方式一的yml文件注释掉,配置方式二所需的yml文件

 方式二:
#      设置路由 规则
      routes:
#        -路由标识
        - id: user-provider-api
          #目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死
          uri: lb://provider
          predicates:
            - # 路径匹配,
            - Path=/prov/**
          filters:
            #路径前缀删除示例:请求/name/bar/foo,StripPrefix=2,去除掉前面两个前缀之后,最后转
#            发到目标服务的路径为/foo
            #前缀过滤,请求地址:http://localhost:8084/usr/hello
            #此处配置去掉1个路径前缀,再配置上面的 Path=/usr/**,就将**转发到指定的微服务
            #因为这个api相当于是服务名,只是为了方便以后nginx的代码加上去的,对于服务提供者
#            service-client来说,不需要这段地址,所以需要去掉
            - StripPrefix=1

2. 重启项目测试

        我们先继续访问之前的方式一的请求路径,结果页面上会显示报错404找不到该接口路径,当我们换成方式二的请求路径进行访问就可以访问成功。

        这就是方式二的测试结果显示 

5.3 方式三:

1. 编写配置类

        我们先将其方式二的yml文件内注释掉,进行编写方式三的yml文件内容。

 

        这是属于我们自定义的配置,所以还需要我们自定义的配置类,写在我们的网关服务中

 FilterEntity.java
package com.yx.gateway.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author hgh
 */
@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class FilterEntity {

    //过滤器对应的Name
    private String name;

    //路由规则
    private Map<String, String> args = new LinkedHashMap<>();

}
 GatewayNacosProperties.java
package com.yx.gateway.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@ConfigurationProperties(prefix = "gateway.nacos")
@Component
public class GatewayNacosProperties {

    private String serverAddr;
    private String dataId;
    private String namespace;
    private String group;

}
PredicateEntity.java
package com.yx.gateway.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author hgh
 */
@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class PredicateEntity {

    //断言对应的Name
    private String name;

    //断言规则
    private Map<String, String> args = new LinkedHashMap<>();

}
 RouteEntity.java
package com.yx.gateway.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.ArrayList;
import java.util.List;

/**
 * @author hgh
 */
@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class RouteEntity {

    //路由id
    private String id;

    //路由断言集合
    private List<PredicateEntity> predicates = new ArrayList<>();

    //路由过滤器集合
    private List<FilterEntity> filters = new ArrayList<>();

    //路由转发的目标uri
    private String uri;

    //路由执行的顺序
    private int order = 0;

}
DynamicRoutingConfig.java
package com.yx.gateway;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yx.gateway.pojo.FilterEntity;
import com.yx.gateway.pojo.GatewayNacosProperties;
import com.yx.gateway.pojo.PredicateEntity;
import com.yx.gateway.pojo.RouteEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * 此类实现了Spring Cloud Gateway + nacos 的动态路由,
 * 它实现一个Spring提供的事件推送接口ApplicationEventPublisherAware
 */
@SuppressWarnings("all")
@Slf4j
@Component
public class DynamicRoutingConfig implements ApplicationEventPublisherAware {

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    @Autowired
    private GatewayNacosProperties gatewayProperties;

    @Autowired
    private ObjectMapper mapper;

    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    /**
     * 这个方法主要负责监听Nacos的配置变化,这里先使用参数构建一个ConfigService,
     * 再使用ConfigService开启一个监听,
     * 并且在监听的方法中刷新路由信息。
     */
    @Bean
    public void refreshRouting() throws NacosException {
        //创建Properties配置类
        Properties properties = new Properties();
        System.out.println(gatewayProperties);
        //设置nacos的服务器地址,从配置类GatewayProperties中获取
        properties.put(PropertyKeyConst.SERVER_ADDR, gatewayProperties.getServerAddr());
        //设置nacos的命名空间,表示从具体的命名空间中获取配置信息,不填代表默认从public获得
        if (gatewayProperties.getNamespace() != null) {
            properties.put(PropertyKeyConst.NAMESPACE, gatewayProperties.getNamespace());
        }
        //根据Properties配置创建ConfigService类
        ConfigService configService = NacosFactory.createConfigService(properties);
        //获得nacos中已有的路由配置
        String json = configService.getConfig(gatewayProperties.getDataId(), gatewayProperties.getGroup(), 5000);
        this.parseJson(json);

        //添加监听器,监听nacos中的数据修改事件
        configService.addListener(gatewayProperties.getDataId(), gatewayProperties.getGroup(), new Listener() {
            @Override
            public Executor getExecutor() {
                return null;
            }

            /**
             * 用于接收远端nacos中数据修改后的回调方法
             */
            @Override
            public void receiveConfigInfo(String configInfo) {
                log.warn(configInfo);
                //获取nacos中修改的数据并进行转换
                parseJson(configInfo);
            }
        });

    }

    /**
     * 解析从nacos读取的路由配置信息(json格式)
     */
    public void parseJson(String json) {
        log.warn("从Nacos返回的路由配置(JSON格式):" + json);
        boolean refreshGatewayRoute = JSONObject.parseObject(json).getBoolean("refreshGatewayRoute");
        if (refreshGatewayRoute) {
            List<RouteEntity> list = JSON.parseArray(JSONObject.parseObject(json).getString("routeList")).toJavaList(RouteEntity.class);
            for (RouteEntity route : list) {
                update(assembleRouteDefinition(route));
            }
        } else {
            log.warn("路由未发生变更");
        }
    }


    /**
     * 路由更新
     */
    public void update(RouteDefinition routeDefinition) {

        try {
            this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()));
            log.warn("路由删除成功:" + routeDefinition.getId());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        try {
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
            log.warn("路由更新成功:" + routeDefinition.getId());
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

    }

    /**
     * 路由定义
     */
    public RouteDefinition assembleRouteDefinition(RouteEntity routeEntity) {

        RouteDefinition definition = new RouteDefinition();

        // ID
        definition.setId(routeEntity.getId());

        // Predicates
        List<PredicateDefinition> pdList = new ArrayList<>();
        for (PredicateEntity predicateEntity : routeEntity.getPredicates()) {
            PredicateDefinition predicateDefinition = new PredicateDefinition();
            predicateDefinition.setArgs(predicateEntity.getArgs());
            predicateDefinition.setName(predicateEntity.getName());
            pdList.add(predicateDefinition);
        }
        definition.setPredicates(pdList);

        // Filters
        List<FilterDefinition> fdList = new ArrayList<>();
        for (FilterEntity filterEntity : routeEntity.getFilters()) {
            FilterDefinition filterDefinition = new FilterDefinition();
            filterDefinition.setArgs(filterEntity.getArgs());
            filterDefinition.setName(filterEntity.getName());
            fdList.add(filterDefinition);
        }
        definition.setFilters(fdList);

        // URI
        URI uri = UriComponentsBuilder.fromUriString(routeEntity.getUri()).build().toUri();
        definition.setUri(uri);

        return definition;
    }

}
 2. 导入所需依赖

        导入阿里的阿里的fastjson的依赖,因为配置类中需要

<!--           导入阿里的fastjson的依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.35</version>
        </dependency>

         导入依赖之后记得更新刷新配置类防止报错。

 3. 在nacos中创建一个json文件

        我们进入nacos的官网中,在配置中心中创建一个json的配置文件,配置文件的名称要与yml文件中的id一致

json内容
{
  "refreshGatewayRoute": true,
  "routeList": [
    {
      "id": "consumer-api",
      "predicates": [
        {
          "name": "Path",
          "args": {
            "_genkey_0": "/cum/**"
          }
        }
      ],
      "filters": [
        {
          "name": "StripPrefix",
          "args": {
            "_genkey_0": "1"
          }
        }
      ],
      "uri": "lb://consumer",
      "order": 0
    },
    {
      "id": "provider-api",
      "predicates": [
        {
          "name": "Path",
          "args": {
            "_genkey_0": "/pvr/**"
          }
        }
      ],
      "filters": [
        {
          "name": "StripPrefix",
          "args": {
            "_genkey_0": "1"
          }
        }
      ],
      "uri": "lb://provider",
      "order": 0
    }
  ]
}
4. 测试效果

        我们重新启动项目,在网页中进行测试效果

         首先我们访问指定的请求路径测试,测试结果是能够成功访问

        当我们前去把在nacos官网将该json文件中生产者的访问路径进行该功重新访问 

        我们更新之后先访问没更改前的请求路径,在访问更改后的请求路径。 

        这就是我们的动态路由的实现方式 


 🎉🎉本期的博客分享到此结束🎉🎉

📚📚各位老铁慢慢消化📚📚

🎯🎯下期博客博主会带来新货🎯🎯

🎁三连加关注,阅读不迷路 !🎁

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

独立站上传产品的方式有哪些?如何快速上品?采集、同步、复制

简介&#xff1a; 搭建新独立站时&#xff0c;传统的复制粘贴将花费大量时间和精力且效率低下&#xff1b;而借助本应用&#xff0c;可有效地避免不必要的人力浪费。只需简单的几个步骤&#xff0c;加上少量的等待时间&#xff0c;即可轻松地将店铺数据从任何商店复制到您的商…

JAVA基础学习笔记-day16-网络编程

JAVA基础学习笔记-day16-网络编程 1. 网络编程概述1.1 软件架构1.2 网络基础 2. 网络通信要素2.1 如何实现网络中的主机互相通信2.2 通信要素一&#xff1a;IP地址和域名2.2.1 IP地址2.2.2 域名 2.3 通信要素二&#xff1a;端口号2.4 通信要素三&#xff1a;网络通信协议 3. 谈…

2024洗地机哪个牌子值得买?洗地机选购指南

在清洁家电的这个市场&#xff0c;洗地机可以说是勇往直前的&#xff0c;不仅在于它高效的深度清洁&#xff0c;还有要考虑它的时间&#xff0c;以及省力方面。在这个洗地机的市场不断地越做越大中&#xff0c;我们在考虑洗地机的配置以及性能上往往没有任何头绪。无线洗地机在…

全链路压力测试有哪些主要作用

全链路压力测试是在软件开发和维护过程中不可或缺的一环&#xff0c;尤其在复杂系统和高并发场景下显得尤为重要。下面将详细介绍全链路压力测试的主要作用。 一、全链路压力测试概述 全链路压力测试是指对软件系统的全部组件(包括前端、后端、数据库、网络、中间件等)在高负载…

redis的使用、打开、关闭的详细介绍

redis的使用、打开、关闭的详细介绍 1.安装redis cd / cd opt/ wget https://download.redis.io/releases/redis-5.0.5.tar.gz 2.解压redis tar xzf redis-5.0.5.tar.gz 3.执行make cd redis-5.0.5/ make 如果出现找不到make的情况就yum install -y make 如果没有gcc就…

phpstorm配置ftp

1 选择设置ftp 2设置自动上传

PACS医学影像报告管理系统源码带CT三维后处理技术

PACS从各种医学影像检查设备中获取、存储、处理影像数据&#xff0c;传输到体检信息系统中&#xff0c;生成图文并茂的体检报告&#xff0c;满足体检中心高水准、高效率影像处理的需要。 自主知识产权&#xff1a;拥有完整知识产权&#xff0c;能够同其他模块无缝对接 国际标准…

Java BIO、NIO(通信/群聊系统、零拷贝)、AIO

Java BIO、NIO(通信/群聊系统、零拷贝)、AIO BIO、NIO、AIO特点和场景 BIO&#xff08;Blocking I/O&#xff09;、NIO&#xff08;Non-blocking I/O&#xff09;、AIO&#xff08;Asynchronous I/O&#xff09;是Java中用于处理I/O操作的三种不同的I/O模型&#xff0c;它们具…

Selenium自动化程序被检测为爬虫,怎么屏蔽和绕过

Selenium 操作被屏蔽 使用selenium自动化网页时&#xff0c;有一定的概率会被目标网站识别&#xff0c;一旦被检测到&#xff0c;目标网站会拦截该客户端做出的网页操作。 比如淘宝和大众点评的登录页&#xff0c;当手工打开浏览器&#xff0c;输入用户名和密码时&#xff0c…

腾讯云CVM购买实例

1、购买云服务器CVM 我选择的是广州区(此前配置的网络区域为六区,至此完成CVM实例)

OpenCV-20卷积操作

一、什么是图像卷积 图像卷积就是卷积在图像上按照滑动遍历像素时不断的相乘求和的过程。 绿色为图片&#xff0c; 黄色为卷积核&#xff0c; 粉色为最终得到的卷积特征。 二、步长 步长就是卷积核在图像上移动的步幅&#xff0c;每次移动一个方格则步幅为1。且一般为1。 若…

python第一节:简介、下载、安装

第一节&#xff1a;简介、下载、安装 简介&#xff1a; Python 的创造者吉多范罗苏姆。 Python 是一款易于学习、跨平台语言、免费和开源、的编程语言。 有丰富的第三方模块库&#xff0c;应用领域非常广泛。常用于数据采集、数据分析和计算、数据挖掘、人工智能等。 下载…

blob分析找出感兴趣区域,求其轮廓再用差分找缺陷

*关闭程序计数器,图形变量,窗口图形更新 dev_update_off() * Image Acquisition 01: Code generated by Image Acquisition 01 ImageFiles : [] ImageFiles[0] : 1.bmp ImageFiles[1] : 2.bmp for Index : 0 to |ImageFiles| - 1 by 1*读取一张图像read_image (Image, ImageFil…

【NVIDIA】Jetson Orin Nano系列:烧写Ubuntu22.04

1、简介 最新的sdk-manager已经可以安装到Ubuntu22.0&#xff0c;也支持在 Jetson Orin Nano 上烧写Ubuntu22.04。 官网介绍&#xff1a;https://developer.nvidia.com/sdk-manager 2、版本介绍 JetPack版本&#xff1a;https://developer.nvidia.com/embedded/jetpack-ar…

使用ros_arduino_bridge控制机器人底盘

使用ros_arduino_bridge控制机器人底盘 搭建了ROS分布式环境后,将ros_arduino_bridge功能包上传至Jetson nano&#xff0c;就可以在PC端通过键盘控制小车的运动了。实现流程如下&#xff1a; 系统准备&#xff1b;下载程序&#xff1b;程序修改&#xff1b;分别启动PC与树莓派…

​​​​​​​Lontium #LT8911EXB适用于MIPIDSI/CSI转EDP应用方案,分辨率高达2560x1440@60HZ 。

1.描述 LT8911EXB是一款高性能 MIPIDSI/CSI到eDP转换器&#xff0c;单端口MIPI接收器有1个时钟通道和4个数据通道&#xff0c;每个数据通道最大运行2.0Gbps&#xff0c;最大输入带宽为8.0Gbps。转换器解码输入MIPI RGB16/18/24/30/36bpp、YUV422 16/20/24bpp、YUV420 12bpp包&…

Python Matplotlib 库使用基本指南

简介 Matplotlib 是一个广泛使用的 Python 数据可视化库&#xff0c;它可以创建各种类型的图表、图形和可视化效果。无论是简单的折线图还是复杂的热力图&#xff0c;Matplotlib 提供了丰富的功能来满足我们的数据可视化需求。本指南将详细介绍如何安装、基本绘图函数以及常见…

现代密码学 考点复盘

现代密码学 考点汇总&#xff08;上&#xff09; 写在最前面考试范围一、给一个简单的方案&#xff0c;判断是否cca安全二、随机预言机模型之下的简单应用 考试题目1.证明CBC方案是CPA安全的2. 证明哈希函数的抗碰撞性3. CBC-MAC安全&#xff1a;证明CPA安全的对称密钥加密方案…

Redis缓存使用问题

数据一致性 只要使用到缓存,无论是本地内存做缓存还是使用 redis 做缓存,那么就会存在数据同步的问题。 以 Tomcat 向 MySQL 中写入和删改数据为例,来解释数据的增删改操作具体是如何进行的。 我们分析一下几种解决方案, 1、先更新缓存,再更新数据库 2、先更新数据库,…

Vue项目nginx部署到线上,访问时加前缀解决方案

一、业务场景&#xff1a; 最近项目开发完了&#xff0c;需要部署一个测试版本和正式版本到线上&#xff0c;测试版本前面需要加一个dev前缀&#xff0c;遇到了一些坑&#xff0c;分享给大家 二、目前效果 三、具体实现步骤&#xff1a; &#xff08;1&#xff09;实现静态文…