微服务配置管理——动态路由

news2024/9/25 13:21:36

动态路由

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

        要实现动态路由首先要将路由配置保存到Nacos,当Nacos中的路由配置变更时,推送最新配置到网关,实时更新网关中的路由信息。 我们需要完成两件事情:

  • 监听Nacos配置变更的消息
  • 当配置变更时,将最新的路由信息更新到网关路由表

1.1监听Nacos配置变更

在Nacos官网中给出了手动监听Nacos配置变更的SDK:

Nacos Springicon-default.png?t=O83Ahttps://nacos.io/zh-cn/docs/nacos-spring.html如果希望 Nacos 推送配置变更,可以使用 Nacos 动态监听配置接口来实现。

获取配置,用于服务启动的时候从 Nacos 获取配置。

public String getConfig(String dataId, String group, long timeoutMs) throws NacosException

请求参数说明:

参数名参数类型描述
dataIdstring配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。全部字符小写。只允许英文字符和 4 种特殊字符(”.”、”:”、”-”、”_”),不超过 256 字节。
groupstring配置分组,建议填写产品名:模块名(Nacos)保证唯一性,只允许英文字符和4种特殊字符(”.”、”:”、”-”、”_”),不超过128字节。
timeoutlong读取配置超时时间,单位 ms,推荐值 3000。

监听配置,如果希望 Nacos 推送配置变更,可以使用 Nacos 动态监听配置接口来实现。

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

请求参数说明:

参数名

参数类型

描述

dataId

string

配置 ID,保证全局唯一性,只允许英文字符和 4 种特殊字符("."、":"、"-"、"_")。不超过 256 字节。

group

string

配置分组,一般是默认的DEFAULT_GROUP。

listener

Listener

监听器,配置变更进入监听器的回调函数。

示例代码:

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() {
        @Override
        public void receiveConfigInfo(String configInfo) {
        // 配置变更的通知处理
                System.out.println("recieve1:" + configInfo);
        }
        @Override
        public Executor getExecutor() {
                return null;
        }
});

这里核心的步骤有2步:

  • 创建ConfigService,目的是连接到Nacos

  • 添加配置监听器,编写配置变更的通知处理逻辑

        由于我们采用了spring-cloud-starter-alibaba-nacos-config自动装配,因此ConfigService已经在com.alibaba.cloud.nacos.NacosConfigAutoConfiguration中自动创建好了:

NacosConfigManager中是负责管理Nacos的ConfigService的,具体代码如下:

因此,只要我们拿到NacosConfigManager就等于拿到了ConfigService。

第二步,编写监听器。虽然官方提供的SDK是ConfigService中的addListener,不过项目第一次启动时不仅仅需要添加监听器,也需要读取配置,因此建议使用的API是这个:

String getConfigAndSignListener(
    String dataId, // 配置文件id
    String group, // 配置组,走默认
    long timeoutMs, // 读取配置的超时时间
    Listener listener // 监听器
) throws NacosException;

        既可以配置监听器,并且会根据dataId和group读取配置并返回。我们就可以在项目启动时先更新一次路由,后续随着配置变更通知到监听器,完成路由更新。

1.2更新路由

更新路由要用到org.springframework.cloud.gateway.route.RouteDefinitionWriter这个接口:

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);

}

这里更新的路由,也就是RouteDefinition,之前我们见过,包含下列常见字段:

  • id:路由id

  • predicates:路由匹配规则

  • filters:路由过滤器

  • uri:路由目的地

将来我们保存到Nacos的配置也要符合这个对象结构,将来我们以JSON来保存,格式如下:

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

以上JSON配置就等同于:

spring:
  cloud:
    gateway:
      routes:
        - id: item
          uri: lb://item-service
          predicates:
            - Path=/items/**,/search/**

1.3实现动态路由

首先, 我们在网关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>

然后在网关gatewayresources目录创建bootstrap.yaml文件,内容如下:

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

接着,修改gatewayresources目录下的application.yml,把之前的路由移除,最终内容如下:

server:
  port: 8080 # 端口
hm:
  jwt:
    location: classpath:hmall.jks # 秘钥地址
    alias: hmall # 秘钥别名
    password: hmall123 # 秘钥文件密码
    tokenTTL: 30m # 登录有效期
  auth:
    excludePaths: # 无需登录校验的路径
      - /search/**
      - /users/login
      - /items/**

然后,在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";
    // 保存更新过的路由id
    private final Set<String> routeIds = new HashSet<>();

    @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(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());
        });
    }
}

接下来,我们直接在Nacos控制台添加路由,路由文件名为gateway-routes.json,类型为json,配置内容如下:

[
    {
        "id": "item",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/items/**", "_genkey_1":"/search/**"}
        }],
        "filters": [],
        "uri": "lb://item-service"
    },
    {
        "id": "cart",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/carts/**"}
        }],
        "filters": [],
        "uri": "lb://cart-service"
    },
    {
        "id": "user",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/users/**", "_genkey_1":"/addresses/**"}
        }],
        "filters": [],
        "uri": "lb://user-service"
    },
    {
        "id": "trade",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/orders/**"}
        }],
        "filters": [],
        "uri": "lb://trade-service"
    },
    {
        "id": "pay",
        "predicates": [{
            "name": "Path",
            "args": {"_genkey_0":"/pay-orders/**"}
        }],
        "filters": [],
        "uri": "lb://pay-service"
    }
]

无需重启网关,稍等几秒钟后,访问地址。

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

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

相关文章

创建游戏暂停菜单

创建用户控件 设置样式 , 加一层 背景模糊 提升UI菜单界面质感 , 按钮用 灰色调 编写菜单逻辑 转到第三人称蓝图 推荐用 Set Input Mode Game And UI , 只用仅UI的话 增强输入响应不了 让游戏暂停的话也可以用 Set Game Paused , 打勾就是暂停 , 不打勾就是继续游戏 , 然后…

0基础如何转行IT

这是一个学习为王的时代&#xff0c;你没有超强的主动学习能力&#xff0c;很容易在千军万马的竞争中落后&#xff0c;甚至被优秀的替代者淘汰。 小白如何转行IT 正所谓业精于专&#xff0c;相较于科班生&#xff0c;非科班转行的在基础方面确实比较薄弱&#xff0c;因此必须…

VMWare虚拟机键盘卡顿

文章目录 环境问题解决办法参考 环境 Windows 11 家庭中文版VMware Workstation 17 ProUbuntu 24.04.1 问题 最近新入手了一台电脑台式机&#xff0c;型号是联想拯救者刃7000K&#xff0c;自带Win11家庭版。主机的CPU是第14代英特尔酷睿i9处理器&#xff0c;异构24核32线程。…

ubuntu 安装minikube,并拉取k8s镜像

虚拟机是vmware17, 系统是ubuntu20.4&#xff0c; minikube是1.23.1&#xff0c; docker是24.0.7&#xff0c; 为什么要装minikube&#xff0c;通常k8s集群是要3台机子以上&#xff0c;而通过minikube&#xff0c;可以在一台机子上搭建出k8s集群&#xff0c;minikube采用的是D…

unraid使用docker安装redis并创建密码

unraid使用docker安装redis并创建密码 一、redis简单介绍 redis基于K-V思路&#xff0c;数据存储在内存中&#xff0c;速度快&#xff0c;高效。 使用时会结合其他数据库如mysql。 二、redis安装 应用市场搜索redis&#xff0c;找下载量最高的一个即可&#xff0c;其中参数只…

5--SpringBoot项目中菜品管理 详解(一)

目录 公共字段自动填充 问题分析 实现思路 代码开发 步骤一 步骤二 功能测试 新增菜品 需求分析和设计 代码开发 文件上传接口 功能测试 公共字段自动填充 问题分析 后台系统的员工管理功能和菜品分类功能的开发&#xff0c;在新增员工或者新增菜品分类时需要设置…

C语言特殊字符串函数和字符函数

特殊字符串函数 strtok(字符串切割函数) 重点&#xff1a;1.delimiters 参数是个字符串&#xff0c;定义了用作分割符的字符集合 2.第一个参数指定一个字符串&#xff0c;里面包含0个或者多个分隔符 3.strtok函数找到str中的分隔符&#xff0c;会把它改成\0&#xff0c;然后…

内衣洗衣机哪个牌子好用?五款业内口碑爆棚产品汇总

内衣裤洗衣机是一种非常实用的洗衣机&#xff0c;可以有效地保护内衣和贴身衣物的质量和卫生&#xff0c;相比于普通的家用大型洗衣机&#xff0c;内衣裤洗衣机在容量、洗涤方式、控制方式和价格等方面有很大的不同之处&#xff0c;如果您经常需要清洗内衣和贴身衣物&#xff0…

无人机蜂群作战会成为未来战争的主要形式吗,该如何反制呢?

无人机蜂群作战在未来战争中确实有可能成为一种重要的作战形式&#xff0c;但是否会成为“主要形式”则取决于多种因素&#xff0c;包括技术发展、战术创新、战略需求以及国际政治和军事格局的变化等。以下是对无人机蜂群作战及其反制措施的详细分析&#xff1a; 一、无人机蜂…

图神经网络(GNN)简单介绍

参考文章:A Gentle Introduction to Graph Neural Networks 仅作为自己学习的笔记 GNN应用领域&#xff1a; 芯片设计 场景分析与问题分析 推荐系统&#xff08;类似抖音&#xff09; 欺诈检测&#xff0c;风控相关 知识图谱 道路交通&#xff0c;动态流量预测 自动驾驶&…

程序员的得力助手:Kimi AI的实战体验引言

引言 作为一名程序员&#xff0c;我们经常需要处理大量信息&#xff0c;从代码调试到文档编写&#xff0c;再到团队协作&#xff0c;每一项任务都需要我们保持高度的专注和效率。在这个过程中&#xff0c;一个得力的助手可以极大地提升我们的工作效率。今天&#xff0c;我想和…

洛谷P2571.传送带

洛谷P2571.传送带 三分模板题 用于单峰函数求极值 一定可以将答案路径分成三段即AE - EF - FD (E和A可能重复&#xff0c;F和D可能重合) E在线段AB上&#xff0c;F在线段CD上 因为有两个不定点EF&#xff0c;因此假设E为参数&#xff0c;三分求F的位置再外层三分求E的位置 …

PMP--三模--解题--1-10

文章目录 9.资源管理1、 [单选] 项目已经准备好开工&#xff0c;资源已经配置好。开发经理随后通知项目经理&#xff0c;由于家庭紧急情况&#xff0c;关键资源不再可用。开发经理表示&#xff0c;所有其他开发人员都被分配到其他项目&#xff0c;任何开发人员都没有能力承担额…

Windows内核编程基础(1)

在前面的文章中&#xff0c;介绍了如何配置开发环境以及如何进行调试。 接下来的几篇文章&#xff0c;将会重点介绍内核编程中所需要了解的一些理论基础。 我写这个系列文章的主要目的是方便以后自己查阅&#xff0c;同时也给正在学习内核开发的小伙伴一些参考&#xff0c;所…

在线PDF转图片怎么转?4种简单转换的方法分享

在线PDF转图片怎么转&#xff1f;在线PDF转图片不仅简化了文档处理流程&#xff0c;还极大地提升了工作效率。无论是教师准备教学材料、学生整理笔记&#xff0c;还是职场人士分享报告&#xff0c;都能通过这一功能轻松实现PDF到图片的转换&#xff0c;确保内容的高清展示与便捷…

OLED(3)字库篇

文章目录 1 显示图片1.1 图片取模 2 字符集与编码2.1 字符编码2.2 ASCII2.3 中文编码2.3.1 GB2312 标准2.3.2 GBK 编码2.3.3 GB18030 2.4 Unicode 字符集和编码2.4.1 UTF-322.4.2 UTF-162.4.3 UTF-8 3 字库 DIY3.1 生成字库3.2 烧录到 W25Qxx 1 显示图片 1.1 图片取模 1&#…

【完整梳理验证】企业微信第三方应用接入全流程java版

企业微信第三方应用接入全流程java版 1. 概念与流程1.1 概念1、企业内部应用2、`第三方应用`3、代开发自建应用1.2 流程1.2.1 全局流程1.2.2 应用配置1.2.3 数据流程2. 核心文档2.1 理解第三方应用开发流程和概念2.1.1 应用开发阶段2.1.2 应用推广阶段2.1.3 基本流程1)前期应用…

【VUE_ruoyi-vue】基于ruoyi-vue框架实现简单的系统通用文件模块

基于ruoyi-vue框架&#xff0c;新增一个简单的系统通用文件模块&#xff0c;服务与各个模块涉及到文件上传信息的记录和相关展示 运行sql,创建数据库表 DROP TABLE IF EXISTS sys_file_info; CREATE TABLE sys_file_info (id int(11) NOT NULL AUTO_INCREMENT COMMENT id,lin…

怎样才能远程了解在iPhone、iPad上看了什么网站、用了什么APP?

有不少家长在网上吐槽&#xff1a; ——自家小孩每天抱着手机看&#xff0c;一看就两三个小时&#xff0c;到底在看什么&#xff1f; ——没有不允许小孩玩手机&#xff0c;但他一玩就一整天&#xff0c;用什么户外活动、家庭活动都吸引不回来。 ——每次问小孩在手机上看什…

【C++掌中宝】用最少的话让你全方位理解内联函数

文章目录 引言1. 什么是内联函数2. 工作原理3. 内联函数的编程风格4. 使用限制5. 内联函数与宏的比较6. 优缺点7. 何时使用内联函数8. 补充9. 总结结语 引言 在C编程中&#xff0c;函数的调用开销是程序运行效率的一个重要影响因素。为了解决频繁调用函数时的性能问题&#xf…