nacos — 动态路由

Nacos 是一个阿里巴巴开源的服务注册中心,广泛用于微服务架构中。它除了支持服务注册和配置管理外,还可以配合网关实现动态路由。动态路由能够根据配置的实时更新动态调整路由规则,避免应用重启,实现路由的灵活管理。

网关的路由配置全部是在项目启动时由org.springframework.cloud.gateway.route.CompositeRouteDefinitionLocator在项目启动的时候加载,并且一经加载就会缓存到内存中的路由表内(一个Map),不会改变。也不会监听路由变更,所以,我们无法利用配置热更新来实现路由更新。

因此,我们必须监听Nacos的配置变更,然后手动把最新的路由更新到路由表中。

1. 监听Nacos配置变更

在Nacos官网中给出了手动监听Nacos配置变更的SDK:https://nacos.io/zh-cn/docs/sdk.html

通过 NacosConfigManager 注册监听器。

2. 更新路由

通过 RouteDefinitionWriter 更新路由。

3. 实现动态路由

3.1 引入依赖

<!--统一配置管理-->
<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>

3.2 配置文件

在网关gatewayresources目录创建bootstrap.yaml文件,配置nacos地址,内容如下:

spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: 192.168.1.101

3.3 监听器

package com.cyt.gateway.route;

@Slf4j // 使用 Lombok 自动生成一个 log 对象,用于记录日志
@Component // 将该类标记为 Spring 组件,使其被 Spring 容器管理
@RequiredArgsConstructor // Lombok 注解,自动生成包含所有 `final` 字段的构造函数,方便依赖注入
public class DynamicRouteLoader {

    // 用于添加或删除网关的路由定义,通过 Spring 注入
    private final RouteDefinitionWriter writer;
    
    // 用于访问 Nacos 配置服务的管理器,通过 Spring 注入
    private final NacosConfigManager nacosConfigManager;

    // Nacos 配置的 dataId,用于标识存储网关路由配置的文件名
    private final String dataId = "gateway-routes.json";
    
    // Nacos 配置的 group,标识配置所属的分组
    private final String group = "DEFAULT_GROUP";
    
    // 用于记录当前加载的路由 ID 集合,便于清除旧的路由配置
    private final Set<String> routeIds = new HashSet<>();

    /**
     * 初始化方法,在类实例化后自动调用,用于注册 Nacos 配置监听器和首次加载路由配置
     * 
     * @throws NacosException 如果 Nacos 配置服务出现异常
     */
    @PostConstruct // 指示该方法在依赖注入完成后自动执行,用于初始化操作
    public void initRouteConfigListener() throws NacosException {
        // 1. 注册监听器并首次拉取配置
        String configInfo = nacosConfigManager.getConfigService()
                .getConfigAndSignListener(dataId, group, 5000, new Listener() {
                    @Override
                    public Executor getExecutor() {
                        // 使用默认线程池执行回调
                        return null;
                    }

                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        // 当检测到路由配置发生变化时,调用 updateConfigInfo 更新路由
                        updateConfigInfo(configInfo);
                    }
                });
        // 2. 初始化时,获取当前配置并更新一次路由
        updateConfigInfo(configInfo);
    }

    /**
     * 更新路由配置的方法
     * 
     * @param configInfo 从 Nacos 获取的路由配置信息(JSON 格式字符串)
     */
    private void updateConfigInfo(String configInfo) {
        log.debug("监听到路由配置变更,{}", configInfo);
        
        // 1. 将 JSON 格式的路由配置字符串解析为 RouteDefinition 对象列表
        List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);
        
        // 2. 清除旧路由配置,避免重复加载
        // 2.1 遍历 routeIds 集合,删除之前加载的路由定义
        for (String routeId : routeIds) {
            writer.delete(Mono.just(routeId)).subscribe(); // 异步删除路由
        }
        // 清空 routeIds 集合,以便保存新的路由 ID
        routeIds.clear();
        
        // 2.2 判断是否有新的路由配置
        if (CollUtils.isEmpty(routeDefinitions)) {
            // 如果没有新路由配置,则直接返回,不进行后续操作
            return;
        }
        
        // 3. 将新的路由定义添加到网关
        routeDefinitions.forEach(routeDefinition -> {
            // 3.1 调用 writer.save() 将新的路由定义保存到网关
            writer.save(Mono.just(routeDefinition)).subscribe(); // 异步保存路由
            // 3.2 记录新加载的路由 ID,便于后续清除旧的路由配置
            routeIds.add(routeDefinition.getId());
        });
    }
}

代码详细解释

  1. @Slf4j:这是 Lombok 的注解,用于自动生成一个 log 对象,可以直接在类中使用 log.debug()log.info() 等方法记录日志,方便跟踪和调试程序的执行过程。

  2. @Component:这是 Spring 的注解,表示这个类是一个 Spring 组件。Spring 会扫描到该类并将其实例化并放入容器中,以便在其他地方进行注入。

  3. @RequiredArgsConstructor:这是 Lombok 的注解,用于自动生成带有 final 字段的构造函数。在本例中,它会为 RouteDefinitionWriterNacosConfigManager 两个字段生成构造方法,使它们可以通过构造函数注入。这种方式可以避免手动写构造函数,减少代码量。

  4. RouteDefinitionWriter:这是 Spring Cloud Gateway 提供的接口,用于动态添加和删除网关的路由定义。通过该接口,可以在不重启网关的情况下更新路由配置。

  5. NacosConfigManager:这是 Alibaba 提供的 Nacos 配置管理器,用于从 Nacos 配置中心获取配置信息和监听配置变更。Nacos 是一个分布式配置中心和服务注册中心,提供配置管理和服务发现功能。

  6. dataIdgroup 字段:这些字段是用来从 Nacos 配置中心读取配置的标识:

    • dataId:指定了配置文件的名称,这里是 "gateway-routes.json",该文件存储了网关的路由配置信息。
    • group:指定了配置文件所属的组名,这里是 "DEFAULT_GROUP"。在 Nacos 中,可以将配置文件按组进行分类管理。
  7. routeIds:用于存储当前已加载的路由 ID 集合,便于在路由更新时清除旧的路由定义,防止新旧路由冲突。

  8. @PostConstruct:这是一个 Java 的注解,表示在依赖注入完成后会自动调用该方法。在本例中,initRouteConfigListener 方法会在 Spring 完成依赖注入后执行,注册一个 Nacos 监听器并首次加载路由配置。

initRouteConfigListener 方法

该方法在初始化时执行,用于向 Nacos 注册监听器,并首次拉取路由配置:

  • 注册监听器:调用 getConfigAndSignListener 方法向 Nacos 注册监听器,指定 dataIdgroup 来监听相应的路由配置。当配置发生变更时,Nacos 会调用 receiveConfigInfo 方法。
  • 首次拉取配置:注册监听器后,同步获取当前配置,接着调用 updateConfigInfo 方法更新路由配置。

updateConfigInfo 方法

该方法接收从 Nacos 获取的配置信息,并更新网关的路由配置:

  1. 反序列化配置:使用 JSONUtil.toList 将 JSON 格式的配置内容转换为 RouteDefinition 对象列表。每个 RouteDefinition 对象代表一个路由定义。

  2. 清除旧路由

    • 遍历 routeIds 集合,删除之前的路由定义,防止新旧路由冲突。
    • 清空 routeIds 集合,以便保存新的路由 ID。
  3. 检查新配置

    • 使用工具类 CollUtils.isEmpty 判断 routeDefinitions 列表是否为空,如果为空则直接结束方法。
  4. 保存新路由

    • 遍历 routeDefinitions 列表,逐个将新的 RouteDefinition 对象保存到网关中。
    • 将每个路由的 ID 添加到 routeIds 集合,便于记录当前加载的路由配置。

3.4 添加路由文件

根据监听器中,所配置的group和dataId,在Nacos控制台添加路由,路由文件名为gateway-routes.json,类型为json

配置内容示例:

[
    {
        // 路由的唯一标识符
        "id": "item",
        
        // 路由的谓词(predicates)配置,用于匹配请求路径
        "predicates": [
            {
                // 使用路径匹配谓词,匹配路径规则
                "name": "Path",
                
                // 路径参数,定义需要匹配的 URL 路径
                "args": {
                    "_genkey_0": "/items/**",     // 匹配路径为 /items/ 下的所有请求
                    "_genkey_1": "/search/**"     // 以及 /search/ 下的所有请求
                }
            }
        ],
        
        // 过滤器(filters)配置,用于对请求或响应进行处理,当前无过滤器
        "filters": [],
        
        // 路由的目标 URI,这里指定了一个逻辑负载均衡的服务名,将请求转发到 item-service
        "uri": "lb://item-service"
    },
    {
        "id": "cart",
        "predicates": [
            {
                "name": "Path",
                "args": {
                    "_genkey_0": "/carts/**"     // 匹配 /carts/ 下的所有请求
                }
            }
        ],
        "filters": [],
        "uri": "lb://cart-service"            // 将请求转发到 cart-service
    }
]

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

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

相关文章

排序 (插入/选择排序)

目录 一 . 排序概念及运用 1.1 排序的概念 1.2 排序的应用 1.3 常见的排序算法 二 . 插入排序 2.1 直接插入排序 2.1 复杂度分析 2.3 希尔排序 2.4 希尔排序时间复杂度分析 三 . 选择排序 3.1 直接选择排序 3.2 堆排序 一 . 排序概念及运用 1.1 排序的概念 排序 : 所…

Unity网络开发基础(part5.网络协议)

目录 前言 网络协议概述 OSI模型 OSI模型的规则 第一部分 物理层 数据链路层 网络层 传输层 第二部分 ​编辑 应用层 表示层 会话层 每层的职能 TCP/IP协议 TCP/IP协议的规则 TCP/IP协议每层的职能 TCP/IP协议中的重要协议 TCP协议 三次握手 四次挥手 U…

框架学习01-Spring

一、Spring框架概述 Spring是一个开源的轻量级Java开发框架&#xff0c;它的主要目的是为了简化企业级应用程序的开发。它提供了一系列的功能&#xff0c;包括控制反转&#xff08;IOC&#xff09;、注入&#xff08;DI&#xff09;、面向切面编程&#xff08;AOP&#xff09;…

Late Chunking×Milvus:如何提高RAG准确率

01. 背景 在RAG应用开发中&#xff0c;第一步就是对于文档进行chunking&#xff08;分块&#xff09;&#xff0c;高效的文档分块&#xff0c;可以有效的提高后续的召回内容的准确性。而对于如何高效的分块是个讨论的热点&#xff0c;有诸如固定大小分块&#xff0c;随机大小分…

【深度学习】InstantIR:图片高清化修复

InstantIR——借助即时生成参考的盲图像修复新方法 作者:Jen-Yuan Huang 等 近年来,随着深度学习和计算机视觉技术的飞速发展,图像修复技术取得了令人瞩目的进步。然而,对于未知或复杂退化的图像进行修复,仍然是一个充满挑战的任务。针对这一难题,研究者们提出了 Insta…

qt获取本机IP和定位

前言&#xff1a; 在写一个天气预报模块时&#xff0c;需要一个定位功能&#xff0c;在网上翻来翻去才找着&#xff0c;放在这里留着回顾下&#xff0c;也帮下有需要的人 正文&#xff1a; 一开始我想着直接调用百度地图的API来定位&#xff0c; 然后我就想先获取本机IP的方…

(C++回溯算法)微信小程序“开局托儿所”游戏

问题描述 给定一个矩阵 A ( a i j ) m n \bm A(a_{ij})_{m\times n} A(aij​)mn​&#xff0c;其中 a i j ∈ { 1 , 2 , ⋯ , 9 } a_{ij}\in\{1,2,\cdots,9\} aij​∈{1,2,⋯,9}&#xff0c;且满足 ∑ i 1 m ∑ j 1 n a i j \sum\limits_{i1}^m\sum\limits_{j1}^na_{ij} i…

数字隔离器与光隔离器有何不同?---腾恩科技

在电子隔离中&#xff0c;两种常用的解决方案是数字隔离器和光学隔离器。两者都旨在电气隔离电路的各个部分&#xff0c;以保护敏感元件免受高压干扰&#xff0c;但它们通过不同的技术实现这一目标。本文探讨了这些隔离器之间的差异&#xff0c;重点介绍了它们的工作原理、优势…

什么是多因素身份验证(MFA)的安全性?

多因素身份验证(MFA)简介 什么是MFA 多因素身份验证(MFA)是一种安全过程&#xff0c;要求用户在授予对系统、应用程序或账户的访问权限之前提供两种或多种形式的验证。仅使用单个因素&#xff08;通常是用户名和密码&#xff09;保护资源会使它们容易受到泄露&#xff0c;添加…

10天进阶webpack---(2)webpack模块兼容性处理

回顾CMJ和ESM的区别 CMJ的本质可以使用一个函数概括 // require函数的伪代码 function require(path){if(该模块有缓存吗){return 缓存结果;}function _run(exports, require, module, __filename, __dirname){// 模块代码会放到这里}var module {exports: {}}_run.call(mod…

Spring源码学习(五):Spring AOP

免责声明 本人还处于学习阶段&#xff0c;如果内容有错误麻烦指出&#xff0c;敬请见谅&#xff01;&#xff01;&#xff01;Demo <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.8<…

【全面解析】Stable Diffusion AI绘画入门教程,轻松掌握,让绘画新手也能快速上手!

前言&#xff1a; 随着人工智能技术的飞速发展&#xff0c;AI绘画领域迎来了一场革命。StableDiffusion作为一款强大的AI绘画工具&#xff0c;以其稳定的图像生成能力和卓越的创造力赢得了广泛关注。本文将向您介绍StableDiffusion的核心特点及其在绘画创作中的广泛应用。同时…

stm32使用串口的轮询模式,实现数据的收发

------内容以b站博主keysking为原型&#xff0c;整理而来&#xff0c;用作个人学习记录。 首先在STM32CubeMX中配置 前期工作省略&#xff0c;只讲重点设置。 这里我配置的是USART2的模式。 会发现&#xff0c;PA2和PA3分别是TX与RX&#xff0c;在连接串口时需要TX对RX&…

openapi回调地址请求不通过

目录 1. 验证url接口get请求本地自测报错 2. 测试回调模式成功不返回结果 3. 测试回调模式返回结果带双引号 对接企业微信 产生会话回调事件 接口问题解决 1. 验证url接口get请求本地自测报错 java.lang.IllegalArgumentException: Last encoded character (before the pa…

软件设计师笔记-数据结构

数据结构 数据元素的集合及元素间的相互关系和构造方法。 线性表的存储结构 顺序存储链式存储 单链表节点 typedef struct node { int data; struct node *link; }NODE, *LinkList; 双向链表 每个节点有两个指针&#xff0c;分别指出直接前驱和直接后继。 循环链表 尾…

【javascript】console 对象提供的方法

文章目录 1、 console.dir() 打印对象2、console.table() 打印数组3、 console.clear() 清理控制台4、console.group() 控制打印组5、console.time() 完成计时 console.log 是一个很好的调试方式。但是 如果我们滥用它&#xff0c;效果反而会适得其反&#xff01;大量打印信息堆…

一:时序数据库-Influx应用

目录 0、版本号 1、登录页面 2、账号基本信息 3、数据库案例 4、可视化 5、java案例 0、版本号 InfluxDB v2.4.0 1、登录页面 http://127.0.0.1:8086/signin 账号&#xff1a;自己账号 密码&#xff1a;自己密码 2、账号基本信息 查看用户id和组织id&#xff01;&…

构建一个导航栏web

<!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>*{margin: 0;padding: 0;}#menu{background-color:purple;width: 100px;height: 50px;}.item{float: left;/* 浮动标签可以让块标签&#xff0c…

JAVA基础:单元测试;注解;枚举;网络编程 (学习笔记)

单元测试 操作步骤&#xff1a; a.导包import org.junit; b.三个注解 Test Before After c.点击Test 运行就可以了 用在不需要控制台输入的情境下&#xff1a;javaweb&#xff0c;框架项目&#xff0c;微服务项目 供开发人员自己做测试。 package com.page…

Node.js-增强 API 安全性和性能优化

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;node.js篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来node.js篇专栏内容:node.js-增强 API 安全性和性能优化 前言 在前几篇文章中&#xff0c;我们已经构建了一个…