网关路由SpringCloudGateway、nacos配置管理(热更新、动态路由)

文章目录

  • 前言
  • 一、网关路由
  • 二、SpringCloudGateway
    • 1. 路由过滤
    • 2. 网关登录校验
      • 2.1 鉴权
      • 2.2 网关过滤器
      • 2.3 登录校验
        • 2.3.1 JWT
        • 2.3.2 登录校验过滤器
    • 3. 微服务从网关获取用户
    • 4. 微服务之间用户信息传递
  • 三、nacos配置管理
    • 问题引入
    • 3.1 配置共享
      • 3.1.1 在Nacos中添加共享配置
      • 3.1.2 拉取共享配置
    • 3.2 配置热更新
      • 问题引入
      • 实现步骤
    • 3.3 动态路由
      • 问题引入
      • 3.3.1 监听nacos配置变更,更新路由表
      • 3.3.2 实现动态路由


前言

前端请求不能直接访问微服务,而是要请求网关(SpringCloudGateway)
网关干什么?路由过滤,登录校验。
nacos既是注册中心也可用作配置管理,用来解决各个微服务块配置文件中相同的配置冗余,配置热更新属性,动态路由等。

一、网关路由

数据在网络间传输,从一个网络传输到另一网络时就需要经过网关来做数据的路由和转发以及数据安全的校验。
在这里插入图片描述
微服务中的网关作用:前端请求不能直接访问微服务,而是要请求网关。

  • 网关可以做安全控制,也就是登录身份校验,校验通过才放行
  • 通过认证后,网关再根据请求判断应该访问哪个微服务,将请求转发过去

二、SpringCloudGateway

网关本身也是一个独立的微服务,所以也需要单独建立一个模块。实现步骤:

  • 创建网关微服务模块
  • 引入SpringCloudGateway、NacosDiscovery依赖
  • 编写启动类
  • 配置网关路由

1. 路由过滤

spring:cloud:gateway:routes:- id: itemuri: lb://item-servicepredicates:- Path=/items/**,/search/**   #请求路径必须符合指定规则

RouteDefinition就是具体的路由规则定义。

  • id:路由的唯一标示
  • predicates:路由断言,其实就是匹配条件
  • filters:路由过滤条件
  • uri:路由目标地址,lb://代表负载均衡,从注册中心获取目标微服务的实例列表,并且负载均衡选择一个访问。

2. 网关登录校验

2.1 鉴权

在网关中保存密钥开发登录校验功能
在这里插入图片描述
问题
1、怎么在转发之前做登录校验
2、校验JWT之后,怎么把用户信息传递给微服务
3、微服务之间怎么传递用户信息

2.2 网关过滤器

在这里插入图片描述
实现原理

  1. 客户端请求进入网关后由HandlerMapping对请求做判断,找到与当前请求匹配的路由规则(Route),然后将请求交给WebHandler去处理。
  2. WebHandler则会加载当前路由下需要执行的过滤器链(Filter chain),然后按照顺序逐一执行过滤器(后面称为Filter)。
  3. 图中Filter被虚线分为左右两部分,是因为Filter内部的逻辑分为pre和post两部分,分别会在请求路由到微服务之前和之后被执行。
  4. 只有所有Filter的pre逻辑都依次顺序执行通过后,请求才会被路由到微服务。
  5. 微服务返回结果后,再倒序执行Filter的post逻辑。
  6. 最终把响应结果返回。

定义一个过滤器,在其中实现登录校验逻辑,并且将过滤器执行顺序定义到NettyRoutingFilter之前?

网关过滤器

  • GatewayFilter:路由过滤器,作用范围比较灵活,可以是任意指定的路由Route.
  • GlobalFilter:全局过滤器,作用范围是所有路由,不可配置。FilteringWebHandler在处理请求时,会将GlobalFilter装饰为GatewayFilter,然后放到同一个过滤器链中,排序以后依次执行。
  • AddRequestHeaderGatewayFilterFacotry: 就是添加请求头的过滤器,可以给请求添加一个请求头并传递到下游微服务。直接在yaml文件中配置

1、自定义GatewayFilter
定义一个类,这个类的名称一定要以GatewayFilterFactory为后缀,这个类继承了AbstractGatewayFilterFactory
2、自定义GlobalFilter
定义一个类,直接实现GlobalFilter接口。

2.3 登录校验

2.3.1 JWT
  • AuthProperties:配置登录校验需要拦截的路径,因为不是所有的路径都需要登录才能访问
  • JwtProperties:定义与JWT工具有关的属性,比如秘钥文件位置
  • SecurityConfig:工具的自动装配
  • JwtTool:JWT工具,其中包含了校验和解析token的功能
  • hmall.jks:秘钥文件
2.3.2 登录校验过滤器

定义一个登录校验的过滤器,实现GlobalFilter接口。

@Component
@RequiredArgsConstructor
@EnableConfigurationProperties(AuthProperties.class)
public class AuthGlobalFilter implements GlobalFilter, Ordered {private final JwtTool jwtTool;private final AuthProperties authProperties;private final AntPathMatcher antPathMatcher = new AntPathMatcher();@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.获取RequestServerHttpRequest request = exchange.getRequest();// 2.判断是否不需要拦截if(isExclude(request.getPath().toString())){// 无需拦截,直接放行return chain.filter(exchange);}// 3.获取请求头中的tokenString token = null;List<String> headers = request.getHeaders().get("authorization");if (!CollUtils.isEmpty(headers)) {token = headers.get(0);}// 4.校验并解析tokenLong userId = null;try {userId = jwtTool.parseToken(token);} catch (UnauthorizedException e) {// 如果无效,拦截ServerHttpResponse response = exchange.getResponse();response.setRawStatusCode(401);return response.setComplete();}// TODO 5.如果有效,传递用户信息//保存用户请求头String userInfo = userId.toString();ServerWebExchange ex = exchange.mutate().request(b -> b.header("user-info",userInfo)).build();// 6.放行return chain.filter(exchange);}private boolean isExclude(String antPath) {for (String pathPattern : authProperties.getExcludePaths()) {if(antPathMatcher.match(pathPattern, antPath)){return true;}}return false;}@Overridepublic int getOrder() {return 0;}
}

3. 微服务从网关获取用户

将用户信息以请求头的方式传递到下游微服务,然后微服务可以从请求头中获取登录用户信息。考虑到微服务内部可能很多地方都需要用到登录用户信息,因此我们可以利用SpringMVC的拦截器来实现登录用户信息获取,并存入ThreadLocal,方便后续使用。
在这里插入图片描述
实现步骤:

  • 改造网关过滤器,在获取用户信息后保存到请求头,转发到下游微服务
  • 编写微服务拦截器,拦截请求获取用户信息,保存到ThreadLocal后放行(因为每个微服务都执行获取用户信息的逻辑,因此统一拦截所有请求,进行处理(AOP的思想))

4. 微服务之间用户信息传递

由于微服务获取用户信息是通过拦截器在请求头中读取,因此要想实现微服务之间的用户信息传递,就必须在微服务发起调用时把用户信息存入请求头。
微服务之间调用是基于OpenFeign来实现的,并不是我们自己发送的请求。我们如何才能让每一个由OpenFeign发起的请求自动携带登录用户信息呢?
Feign中提供的一个拦截器接口:feign.RequestInterceptor
实现方式
定义一个类,实现RequestInterceptor接口,实现接口中的apply方法,用RequestTemplate类来添加请求头,将用户信息保存到请求头中。这样以来,每次OpenFeign发起请求的时候都会调用该方法,传递用户信息。

@Bean
public RequestInterceptor userInfoRequestInterceptor(){return new RequestInterceptor() {@Overridepublic void apply(RequestTemplate template) {// 获取登录用户Long userId = UserContext.getUser();if(userId == null) {// 如果为空则直接跳过return;}// 如果不为空则放入请求头中,传递给下游微服务template.header("user-info", userId.toString());}};
}

在这里插入图片描述

三、nacos配置管理

问题引入

  • 网关路由在配置文件中写死了,如果变更必须重启微服务
  • 某些业务配置在配置文件中写死了,每次修改都要重启服务
  • 每个微服务都有很多重复的配置,维护成本高

nacos: 无需重启即可生效,实现配置热更新。实现动态路由功能,无需重启网关即可修改路由配置。
在这里插入图片描述

3.1 配置共享

3.1.1 在Nacos中添加共享配置

抽取配置文件yaml中重复的模块,如jdbc相关配置(datasource、mybatis-plus)、日志配置(logging)、swagger(knife4j)以及OpenFeign的配置(feign)
在这里插入图片描述
在这里插入图片描述

3.1.2 拉取共享配置

如何去加载nacos中的配置文件?
在这里插入图片描述
实现步骤
1、在模块中引入依赖

  <!--nacos配置管理--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!--读取bootstrap文件--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId></dependency>

2、新建bootstrap.yaml,配置nacos地址

spring:cloud:nacos:server-addr: 192.168.150.101 # nacos地址config:file-extension: yaml # 文件后缀名shared-configs: # 共享配置- dataId: shared-jdbc.yaml # 共享mybatis配置- dataId: shared-log.yaml # 共享日志配置- dataId: shared-swagger.yaml # 共享日志配置

3.2 配置热更新

问题引入

业务相关参数,将来可能会根据实际情况临时调整。例如购物车业务,购物车数量有一个上限,默认是10。

实现步骤

1、在nacos中添加配置属性
2、新建一个属性读取类,在需要使用该属性的业务类中注入。

3.3 动态路由

问题引入

网关的路由配置全部是在项目启动的时候加载,并且一经加载就会缓存到内存中的路由表内(一个Map),不会改变,也不会监听路由变更。

  • 如何监听Nacos配置变更?
  • 如何把路由信息更新到路由表?

3.3.1 监听nacos配置变更,更新路由表

使用 Nacos 动态监听配置接口来实现。

public void addListener(String dataId, String group, Listener listener)

核心点:

  • 创建ConfigService,连接到Nacos(spring-cloud-starter-alibaba-nacos-config自动装配,拿到NacosConfigManager就等于拿到了ConfigService)
  • 添加配置监听器,编写配置变更的通知处理逻辑
String serverAddr = "{serverAddr}";
String dataId = "{dataId}";
String group = "{group}";
// 1.创建ConfigService,连接Nacos
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
ConfigService configService = NacosFactory.createConfigService(properties);
// 2.读取配置
String content = configService.getConfig(dataId, group, 5000);
// 3.添加配置监听器
configService.addListener(dataId, group, new Listener() {@Overridepublic void receiveConfigInfo(String configInfo) {// 配置变更的通知处理System.out.println("recieve1:" + configInfo);}@Overridepublic Executor getExecutor() {return null;}
});

更新路由表

package org.springframework.cloud.gateway.route;
import reactor.core.publisher.Mono;
/*** @author Spencer Gibb*/
public interface RouteDefinitionWriter {/*** 更新路由到路由表,如果路由id重复,则会覆盖旧的路由*/Mono<Void> save(Mono<RouteDefinition> route);/*** 根据路由id删除某个路由*/Mono<Void> delete(Mono<String> routeId);
}

3.3.2 实现动态路由

1、网关gateway引入依赖

<!--统一配置管理-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--加载bootstrap-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

2、在网关gateway的resources目录创建bootstrap.yaml文件

spring:application:name: gatewaycloud:nacos:server-addr: 192.168.150.101config:file-extension: yamlshared-configs:- dataId: shared-log.yaml # 共享日志配置

3、在gateway中定义配置监听器

package com.hmall.gateway.route;import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.hmall.common.utils.CollUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {private final RouteDefinitionWriter writer;private final NacosConfigManager nacosConfigManager;// 路由配置文件的id和分组private final String dataId = "gateway-routes.json";private final String group = "DEFAULT_GROUP";// 保存更新过的路由idprivate final Set<String> routeIds = new HashSet<>();@PostConstructpublic void initRouteConfigListener() throws NacosException {// 1.注册监听器并首次拉取配置String configInfo = nacosConfigManager.getConfigService().getConfigAndSignListener(dataId, group, 5000, new Listener() {@Overridepublic Executor getExecutor() {return null;}@Overridepublic void receiveConfigInfo(String configInfo) {updateConfigInfo(configInfo);}});// 2.首次启动时,更新一次配置updateConfigInfo(configInfo);}private void updateConfigInfo(String configInfo) {log.debug("监听到路由配置变更,{}", configInfo);// 1.反序列化List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);// 2.更新前先清空旧路由// 2.1.清除旧路由for (String routeId : routeIds) {writer.delete(Mono.just(routeId)).subscribe();}routeIds.clear();// 2.2.判断是否有新的路由要更新if (CollUtils.isEmpty(routeDefinitions)) {// 无新路由配置,直接结束return;}// 3.更新路由routeDefinitions.forEach(routeDefinition -> {// 3.1.更新路由writer.save(Mono.just(routeDefinition)).subscribe();// 3.2.记录路由id,方便将来删除routeIds.add(routeDefinition.getId());});}
}

4、在nacos中配置路由信息,路由文件名为gateway-routes.json,类型为json

[{"id": "item","predicates": [{"name": "Path","args": {"_genkey_0":"/items/**", "_genkey_1":"/search/**"}}],"filters": [],"uri": "lb://item-service"},....

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

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

相关文章

Vue 3入门指南

title: Vue 3入门指南 date: 2024/5/23 19:37:34 updated: 2024/5/23 19:37:34 categories: 前端开发 tags: 框架对比环境搭建基础语法组件开发响应式系统状态管理路由配置 第1章&#xff1a;Vue 3简介 1.1 Vue.js的历史与发展 Vue.js由前谷歌工程师尤雨溪&#xff08;Eva…

Java分支结构详解

Java分支结构详解 前言一、if 语句基本语法表示一表示二表示三 代码示例判定一个数字是奇数还是偶数判定一个数字是正数还是负数判定某一年份是否是闰年 注意要点悬垂 else 问题代码风格问题分号问题 二、switch 语句基本语法代码示例根据 day 的值输出星期 注意事项break 不要…

spring常用知识点

1、拦截器和过滤器区别 1. 原理不同&#xff1a; 拦截器是基于java的反射机制&#xff0c;而过滤器采用责任链模式是基于函数回调的。 2. 使用范围不同&#xff1a; 过滤器Filter的使用依赖于Tomcat等容器&#xff0c;导致它只能在web程序中使用 拦截器是一个Sping组件&am…

abs(-2147483648) == 2147483648?

从数学意义上&#xff0c;这是对的。但是&#xff0c;就怕但是。 #include int main() {long long v;v abs(-2147483648);printf("%lld\n", v);return 0; } 输出: -2147483648 我们从source code中一一解开. /* Return the absolute value of I. */ int abs (…

Mongodb介绍及springboot集成增删改查

文章目录 1. MongoDB相关概念1.1 业务应用场景1.2 MongoDB简介1.3 体系结构1.4 数据模型1.5 MongoDB的特点 2. docker安装mongodb3. springboot集成3.1 文件结构3.2 增删改查3.2.1 增加insert3.2.2 保存save3.2.3 更新update3.2.4 查询3.2.5 删除 1. MongoDB相关概念 1.1 业务…

Docker-Android安卓模拟器本地部署并实现远程开发测试

文章目录 1. 虚拟化环境检查2. Android 模拟器部署3. Ubuntu安装Cpolar4. 配置公网地址5. 远程访问小结 6. 固定Cpolar公网地址7. 固定地址访问 本文主要介绍如何在Ubuntu系统使用Docker部署docker-android安卓模拟器&#xff0c;并结合cpolar内网穿透工具实现公网远程访问本地…

51建模网AR虚拟试用,让网购不再只靠想象!

在数字化的浪潮中&#xff0c;网购已成为现代人生活的一部分。然而&#xff0c;传统的网购模式常常因为无法直接试穿、试用商品&#xff0c;导致买家在收到商品后感到失望&#xff0c;特别是面对大件家居产品时&#xff0c;仅凭屏幕上的图片和尺寸描述&#xff0c;很难准确地把…

智能AI愈发强大,企业如何防范AI网络钓鱼攻击

随着AI技术的快速发展&#xff0c;如ChatGPT等智能化工具在各个领域得到了广泛应用。然而&#xff0c;这些工具的普及也给网络安全带来了新的挑战。AI模型的自然语言生成功能使得网络钓鱼攻击更加智能化和隐蔽化&#xff0c;攻击者能够利用AI技术生成高度逼真的欺骗性邮件和其他…

深度学习之基于YoloV5人体姿态摔倒识别分析报警系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景与意义 随着人口老龄化的加剧和人们对健康安全的日益关注&#xff0c;摔倒事件在老年人、幼儿、体育运…

分布式文件系统minIo

分布式文件系统 什么是分布式文件系统 一个计算机无法存储海量的文件&#xff0c;通过网络将若干计算机组织起来共同去存储海量的文件&#xff0c;去接收海量用户的请求&#xff0c;这些组织起来的计算机通过网络进行通信&#xff0c;如下图&#xff1a; 好处&#xff1a; 1、…

怎么ai思维导图自动生成?推荐这三个工具

怎么ai思维导图自动生成&#xff1f;随着人工智能&#xff08;AI&#xff09;技术的不断发展&#xff0c;其在各个领域的应用也日益广泛。在思维导图这一领域&#xff0c;AI技术的引入不仅极大地提高了制图效率&#xff0c;还为用户提供了更多样化、个性化的制图体验。本文将为…

基于消息中间件的异步通信机制在系统解耦中的优化与实现

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; ✨✨ 帅哥美女们&#xff0c;我们共同加油&#xff01;一起进步&am…

GPT-SoVITS语音克隆部署与使用

GPT-SoVITS是一款强大的少量样本语音转换与语音合成开源工具。当前&#xff0c;GPT-SoVITS实现了如下几个方面的功能&#xff1a; 由参考音频的情感、音色、语速控制合成音频的情感、音色、语速可以少量语音微调训练&#xff0c;也可不训练直接推理可以跨语种生成&#xff0c;…

【AI】试用 ai 提取文章内容尝试

电梯产业面临这样一个问题&#xff0c;因为太多的品牌&#xff0c;将近 400 多个&#xff0c;甚至有宝马&#xff0c;奥迪&#xff0c;你敢相信&#xff0c;一家造汽车的造过电梯?不过好像想想也是&#xff0c;电梯是第二大交通工具&#xff0c;电梯从某种意义上来说&#xff…

无网环境禁止 WPS 提示登录,且基本功能按钮可用

目前 WPS 升级后&#xff0c;每次打开都会提示你登录 WPS&#xff0c;并且在未登录之前所有基本功能按钮是置灰状态&#xff0c;无法使用。 如此一来&#xff0c;在内网或无网环境&#xff0c;我们无法登陆 WPS &#xff0c;就给我们的使用带来了极大的不便&#xff0c;那么有没…

全网最全面的由浅到深的Kotlin基础教程(七)

前言 本篇文章接着上一篇文章全网最全面的由浅到深的Kotlin基础教程&#xff08;六&#xff09;继续进阶学习kotlin&#xff0c;建议看完上一篇文章&#xff0c;再来看本篇文章。本篇主要分析一些常用的kotlin函数源码&#xff0c;以及用kotlin简单实现Rxjava的几个操作符。坚…

全志A133 android10 调试vibrator震动马达

一&#xff0c;前提条件 全志使用的马达配置为上电震动&#xff0c;下电停止&#xff0c;需测试硬件是否正常。马达供电最好为独立供电&#xff0c;避免干扰。 二&#xff0c;适配步骤 1. dts中增加马达配置 motor_para {compatible "allwinner,sunxi-vibrator";…

BGP实验:联邦和发射器实验

BGP实验&#xff1a;联邦和发射器实验 一、实验拓扑 二、实验要求及分析 实验要求&#xff1a; 1、AS1存在两个环回&#xff0c;一个地址为192.168.1.0/24&#xff0c;该地址不能再任何协议中宣告&#xff1b; ​ AS3存在两个环回&#xff0c;一个地址为192.168.2.0/24&…

解决ModuleNotFoundError: No module named ‘open_clip‘问题

在使用stable diffusion大模型时&#xff0c;添加一些模型后启动df页面报错&#xff1a;ModuleNotFoundError: No module named open_clip 使用 pip install open_clip命令下载失败&#xff0c;报错&#xff1a; Looking in indexes: https://mirrors.aliyun.com/pypi/simple…

Redis【B站面试题】

前言 2023-07-27 22:44:59 出自B站 灰灰的Java面试 Redis Redis为什么快&#xff1f; 1.纯内存KV操作 Redis的操作都是基于内存的&#xff0c;CPU不是 Redis性能瓶颈,&#xff0c;Redis的瓶颈是机器内存和网络带宽。 在计算机的世界中&#xff0c;CPU的速度是远大于内存的速…