【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: gatewaycloud:nacos:discovery:server-addr: localhost:8848gateway:discovery:locator:enabled: truelower-case-service-id: true

4. 启动类标记使用nacos

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

5. 使用网关的方式

5.1 方式一:

1. 对其yml文件配置

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

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

 2. 测试演示

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

         我们在网页进行测试

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

 5.2 方式二:

 1. 对其yml文件进行配置

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

 方式二:
#      设置路由 规则routes:
#        -路由标识- id: user-provider-api#目标服务地址(uri:地址,请求转发后的地址),会自动从注册中心获得服务的IP,不需要手动写死uri: lb://providerpredicates:- # 路径匹配,- 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 {//过滤器对应的Nameprivate 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 {//断言对应的Nameprivate 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 {//路由idprivate String id;//路由断言集合private List<PredicateEntity> predicates = new ArrayList<>();//路由过滤器集合private List<FilterEntity> filters = new ArrayList<>();//路由转发的目标uriprivate 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 {@Autowiredprivate RouteDefinitionWriter routeDefinitionWriter;@Autowiredprivate GatewayNacosProperties gatewayProperties;@Autowiredprivate ObjectMapper mapper;private ApplicationEventPublisher applicationEventPublisher;@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}/*** 这个方法主要负责监听Nacos的配置变化,这里先使用参数构建一个ConfigService,* 再使用ConfigService开启一个监听,* 并且在监听的方法中刷新路由信息。*/@Beanpublic 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() {@Overridepublic Executor getExecutor() {return null;}/*** 用于接收远端nacos中数据修改后的回调方法*/@Overridepublic 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();// IDdefinition.setId(routeEntity.getId());// PredicatesList<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);// FiltersList<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);// URIURI 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文件中生产者的访问路径进行该功重新访问 

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

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


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

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言结构体的字节对齐

C语言结构体的字节对齐 什么是字节对齐 首先来看下面的程序&#xff1a; #include <stdio.h>typedef struct n1{int a;char b;char c; } N_stru1;typedef struct n2{char b;int a;char c; } N_stru2;int main() {N_stru1 n1;N_stru2 n2;printf("%d\n", siz…

AIGC视频生成:Pika1.0快速入门详解

Pika1.0快速入门详解 一、简介二、登录三、参数设置1、改变画面大小&#xff08;Aspect ratio&#xff09;2、改变帧数大小&#xff08;Frames per second&#xff09;3、镜头平移&#xff08;Camera control&#xff09;4、画面运动控制&#xff08;Strength of motion&#x…

c++ 经典服务器开源项目 Tinywebserver学习笔记

learning make me happy---更新中 疑问部分ENGINEInnoDB 存储引擎指定为innoDB的作用的意义&#xff1f; 报错部分fatal error: mysql/mysql.h: No such file or directory&#xff1f;进程结束后还占用大量内存&#xff1f; 知识学习和查漏补缺epoll_create&#xff08;5&…

面试算法110:所有路径

题目 一个有向无环图由n个节点&#xff08;标号从0到n-1&#xff0c;n≥2&#xff09;组成&#xff0c;请找出从节点0到节点n-1的所有路径。图用一个数组graph表示&#xff0c;数组的graph[i]包含所有从节点i能直接到达的节点。例如&#xff0c;输入数组graph为[[1&#xff0c…

机器学习 -- 余弦相似度

场景 我有一个 页面如下&#xff08;随便找的&#xff09;&#xff1a; 我的需求是拿到所有回答的链接&#xff0c; 再或者我在找房子网上&#xff0c;爬到所有的房产信息&#xff0c;我们并不想做过多的处理&#xff0c;我只要告诉程序&#xff0c;请帮我爬一个类似 xxx 相似…

golang并发安全-select

前面说了golang的channel&#xff0c; 今天我们看看golang select 是怎么实现的。 数据结构 type scase struct {c *hchan // chanelem unsafe.Pointer // 数据 } select 非默认的case 中都是处理channel 的 接受和发送&#xff0c;所有scase 结构体中c是用来存储…

秒变办公达人,只因用了这5款在线协同文档app!

在日常工作中&#xff0c;我们不可避免地需要处理各种文档&#xff0c;有时你可能会为如何高效地管理这些文档而感到烦恼&#xff0c;或是不知道如何挑选合适的在线文档工具&#xff1f; 不用担心&#xff01;在这篇文章中&#xff0c;我们将介绍5个好用的在线文档工具App&…

Hive精选10道面试题

1.Hive内部表和外部表的区别&#xff1f; 内部表的数据由Hive管理&#xff0c;外部表的数据不由Hive管理。 在Hive中删除内部表后&#xff0c;不仅会删除元数据还会删除存储数据&#xff0c; 在Hive中删除外部表后&#xff0c;只会删除元数据但不会删除存储数据。 内部表一旦…

图像分类任务的可视化脚本,生成类别json字典文件

1. 前言 之前的图像分类任务可视化&#xff0c;都是在train脚本里&#xff0c; 用torch中dataloader将图片和类别加载&#xff0c;然后利用matplotlib库进行可视化。 如这篇文章中&#xff1a;CNN 卷积神经网络对染色血液细胞分类(blood-cells) 在分类任务中&#xff0c;必定…

SSM+mysql电影推荐系统-计算机毕业设计源码030873

目 录 摘 要 Abstract 第1章 前 言 1.1 研究背景 1.2 研究现状 1.3 系统开发目标 第2章 技术与原理 2.1 开发技术 2 2.2 ssm框架介绍 2 2.3 MySQL数据库 2 2.4 B/S结构 2 第3章 需求分析 3.1 需求分析 3.2 系统可行性分析 3.3 项目设计目标与原则 3.4…

软件测试工程师经典面试题总结

一、接口测试如何设计测试用例&#xff1f; 首先&#xff0c;接口测试用例与其他测试用例是一样的&#xff0c;都是为了证明程序存在错误&#xff0c;其出发点相同&#xff1b;接口测试用例的对象是接口&#xff0c;需要验证各个系统及组件间的接口&#xff1b;其三是接口测试的…

2019年认证杯SPSSPRO杯数学建模B题(第一阶段)外星语词典全过程文档及程序

2019年认证杯SPSSPRO杯数学建模 基于方差分布的方法对未知语言文本中重复片段的自动搜索问题的研究 B题 外星语词典 原题再现&#xff1a; 我们发现了一种未知的语言&#xff0c;现只知道其文字是以 20 个字母构成的。我们已经获取了许多段由该语言写成的文本&#xff0c;但…