SpringCloud-gateway编码实现路由策略的自动刷新,动态路由

news2024/9/22 1:37:57

文章目录

  • 一、概述
    • 1、背景
    • 2、实现思路
  • 二、编码实现
    • 1、nacos配置刷新公共类
    • 2、自定义RouteDefinition
    • 3、route缓存类
    • 4、动态更新路由网关service
    • 5、动态路由加载类
  • 三、测试

一、概述

1、背景

gateway可以配置路由断言过滤器,但是通常一个微服务体系下,一个gateway网关对应多个微服务,如果上线一个新的微服务或者修改一个微服务,修改网关路由配置之后,通常需要重启网关之后,路由配置才会生效,这样的影响会比较大。

考虑实现gateway的动态路由,不重启网关即可生效路由。

2、实现思路

基于nacos的配置,实现修改nacos的配置之后,通知给网关,在网关里编写逻辑,实现路由的自动刷新。

二、编码实现

1、nacos配置刷新公共类

/**
 * nacos配置加载器
 */
public interface NacosPropertiesLoader {


    /**
     * 获取dataId
     */
    String getDataId();

    /**
     * 配置刷新的回调
     */
    void getConfigData(String configData);
}


import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.AbstractListener;
import com.alibaba.nacos.api.exception.NacosException;
import org.springframework.beans.BeansException;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;

import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

@Configuration
public class NacosConfigHandler implements ApplicationListener<ApplicationReadyEvent>, ApplicationContextAware {

    private final NacosConfigManager nacosConfigManager;

    List<NacosPropertiesLoader> nacosPropertiesLoaderList = new CopyOnWriteArrayList<>();

    private String groupId;


    public NacosConfigHandler(NacosConfigManager nacosConfigManager) {
        this.nacosConfigManager = nacosConfigManager;
    }


    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        // 容器环境准备完毕了,加载配置
        ConfigService configService = nacosConfigManager.getConfigService();

        try {
            // 加载所有的配置,并设置监听器
            for (NacosPropertiesLoader nacosPropertiesLoader : nacosPropertiesLoaderList) {

                nacosPropertiesLoader.getConfigData(
                        configService.getConfig(nacosPropertiesLoader.getDataId(), groupId, 3000)
                );

                configService.addListener(nacosPropertiesLoader.getDataId(), groupId, new AbstractListener() {
                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        nacosPropertiesLoader.getConfigData(configInfo);
                    }
                });

            }
        } catch (NacosException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, NacosPropertiesLoader> nacosPropertiesLoaderBeans = applicationContext.getBeansOfType(NacosPropertiesLoader.class);
        if (nacosPropertiesLoaderBeans == null) {
            return;
        }
        for (NacosPropertiesLoader value : nacosPropertiesLoaderBeans.values()) {
            nacosPropertiesLoaderList.add(value);
        }

        // 从配置中读取nacos.group  nacos的groupId
        groupId = applicationContext.getEnvironment().getProperty("nacos.group");

    }
}


2、自定义RouteDefinition

import com.alibaba.fastjson2.JSON;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.route.RouteDefinition;

import java.util.List;

/**
 * 自定义RouteDefinition
 */
public class MyRouteDefinition extends RouteDefinition {
    /**
     * 路由状态 0禁用 1启用
     */
    private Integer status;

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public static List<MyRouteDefinition> load(String config) {
        if (StringUtils.isEmpty(config)) {
            return null;
        }
        List<MyRouteDefinition> myRouteDefinitions = JSON.parseArray(config, MyRouteDefinition.class);
        return myRouteDefinitions;

    }

}

3、route缓存类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

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

import static java.util.Collections.synchronizedMap;

/**
 * route缓存自定义
 */
@Component
public class MyInMemoryRouteDefinitionRepository implements RouteDefinitionRepository {
    private static final Logger log = LoggerFactory.getLogger(DynamicRouteService.class);

    private Map<String, RouteDefinition> routes = synchronizedMap(new LinkedHashMap<String, RouteDefinition>());


    public void refreshRoute(List<MyRouteDefinition> routeDefinitions) {
        Map<String, RouteDefinition> newRoutes = synchronizedMap(new LinkedHashMap<String, RouteDefinition>());
        routeDefinitions.forEach(r -> newRoutes.put(r.getId(), r));
        routes = newRoutes;
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return route.flatMap(r -> {
            if (ObjectUtils.isEmpty(r.getId())) {
                return Mono.error(new IllegalArgumentException("id may not be empty"));
            }
            routes.put(r.getId(), r);
            return Mono.empty();
        });
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return routeId.flatMap(id -> {
            if (routes.containsKey(id)) {
                routes.remove(id);
                return Mono.empty();
            }
//            return Mono.defer(() -> Mono.error(new NotFoundException("RouteDefinition not found: " + routeId)));
            log.warn("RouteDefinition not found: " + routeId);
            return Mono.empty();
        });
    }

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        Map<String, RouteDefinition> routesSafeCopy = new LinkedHashMap<>(routes);
        return Flux.fromIterable(routesSafeCopy.values());
    }
}

4、动态更新路由网关service

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * 动态更新路由网关service
 * 1)实现一个Spring提供的事件推送接口ApplicationEventPublisherAware
 * 2)提供动态路由的基础方法,可通过获取bean操作该类的方法。该类提供新增路由、更新路由、删除路由,然后实现发布的功能。
 */
@Service
public class DynamicRouteService implements ApplicationEventPublisherAware {

    private static final Logger log = LoggerFactory.getLogger(DynamicRouteService.class);

    @Autowired
    private MyInMemoryRouteDefinitionRepository repository;

    /**
     * 发布事件
     */

    private ApplicationEventPublisher publisher;

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

    /**
     * 删除路由
     *
     * @param id
     * @return
     */
    public synchronized void delete(String id) {
        try {
            repository.delete(Mono.just(id)).subscribe();
//            this.publisher.publishEvent(new RefreshRoutesEvent(this));
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 更新路由
     *
     * @param definition
     * @return
     */
    public synchronized String update(RouteDefinition definition) {
        try {
            log.info("gateway update route {}", definition);
        } catch (Exception e) {
            return "update fail,not find route  routeId: " + definition.getId();
        }
        try {
            repository.save(Mono.just(definition)).subscribe();
//            this.publisher.publishEvent(new RefreshRoutesEvent(this));
            return "success";
        } catch (Exception e) {
            return "update route fail";
        }
    }

    /**
     * 增加路由
     *
     * @param definition
     * @return
     */
    public synchronized String add(RouteDefinition definition) {
        log.info("gateway add route {}", definition);
        try {
            repository.save(Mono.just(definition)).subscribe();
//            this.publisher.publishEvent(new RefreshRoutesEvent(this));
        } catch (Exception e) {
            log.warn(e.getMessage(),e);
        }
        return "success";
    }

    public void refreshRoutes(List<MyRouteDefinition> load) {
        repository.refreshRoute(load);
    }
}

5、动态路由加载类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class DynamicRouteLoader implements NacosPropertiesLoader {

    private static final Logger log = LoggerFactory.getLogger(DynamicRouteLoader.class);


    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private DynamicRouteService dynamicRouteService;
	// nacos上配置的dataID
    private static final String DataId = "gateway_routes";

    @Override
    public String getDataId() {
        return DataId;
    }

    @Override
    public void getConfigData(String configData) {
        log.info("加载到路由配置:{}", log);
        // 动态加载路由
        List<MyRouteDefinition> load = MyRouteDefinition.load(configData);

        if (load == null || load.size() == 0) {
            log.info("未加载到routes");
            return;

        }

        dynamicRouteService.refreshRoutes(load);

        // 路由刷新事件,让路由生效
        this.applicationContext.publishEvent(new RefreshRoutesEvent(this));
    }
}

三、测试

在nacos上创建一个配置(注意dataid和group):
在这里插入图片描述
内容需要按照json格式进行配置(其他格式需要手写配置的解析方法)

[{
	"filters": [],
	"id": "payment_routh",
	"metadata": {},
	"order": 0,
	"predicates": [{
		"args": {
			"_genkey_0": "/test/**"
		},
		"name": "Path"
	}],
	"uri": "lb://test1"
},
{
	"filters": [],
	"id": "payment_routh2",
	"metadata": {},
	"order": 0,
	"predicates": [{
		"args": {
			"_genkey_0": "/test2/**"
		},
		"name": "Path"
	}],
	"uri": "lb://test2"
}

]

修改该配置会自动更新路由信息。

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

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

相关文章

KCTF 闯关游戏:1 ~ 7 关

前言 看雪CTF平台是一个专注于网络安全技术竞赛的在线平台&#xff0c;它提供了一个供网络安全爱好者和技术专家进行技术交流、学习和竞技的环境。CTF&#xff08;Capture The Flag&#xff0c;夺旗赛&#xff09;是网络安全领域内的一种流行竞赛形式&#xff0c;起源于1996年…

虚拟机Linux系统字体太小怎么办?

每次用虚拟机的Linux系统时&#xff0c;都觉得字体小得伤眼睛&#xff0c;所以就尝试找了下&#xff0c;没想到可以直接用大号字体&#xff0c;这感觉好多啦~ 这里针对centOS的图形界面&#xff0c;非常简单&#xff0c;见下面的图&#xff1a; 应用程序 --> 系统工具 --&…

ELK三个开源软件的工作原理

一、Elasticsearch Elasticsearch的工作原理主要涉及其数据处理、索引机制、查询过程以及集群管理等方面。一下是对Elasticsearch工作原理的详细解析&#xff1a; 1. 数据处理 1.1 数据导入 Elasticsearch支持多种数据源&#xff0c;包括直接输入、通过Logstash和Beats等工具…

js日期处理库--dayjs

js中处理日期是一件比较麻烦的事情&#xff0c;这里推荐使用day.js库来处理&#xff0c;文档:Day.js中文网 引入库 如果没有引入过dayjs,需要先执行npm install,然后import就能使用了 npm install dayjs import dayjs from dayjsconsole.log(dayjs().format()) 如果使用的…

leetcode日记(67)单词搜索

太坑了&#xff01;老是时间超限&#xff0c;不是时间超限就是内存超限&#xff01; 思路很简单&#xff0c;就是先遍历整个网格寻找开头&#xff0c;然后上下左右搜寻找下一个字母&#xff0c;引用递归。 最终看边答案边写出来的&#xff1a; class Solution { public:bool …

36-《茅膏菜》:自然奇境中的捕虫艺术家

茅膏菜 腺毛闪烁诱昆虫&#xff0c;粘液缠绕捕食成。 绿叶如网藏陷阱&#xff0c;茅膏菜展捕虫情。 中文名&#xff1a;茅膏菜 拉丁名&#xff1a;Drosera peltata Thunb. 别名&#xff1a;捕虫草、落地珍珠、一粒金丹、苍蝇草、珍珠草 科属&#xff1a;茅膏菜科茅膏菜属 生…

词向量,位置嵌入;归一化;自注意力层;投影;残差连接:防止梯度消失;MLP;

目录 词向量,位置嵌入 归一化 自注意力层 我们该如何处理Q、K和V向量呢 投影 残差连接:防止梯度消失 MLP 层归一化处理 GELU激活函数 MLP Weights 和MLP Projection Weights区别和联系 一、MLP Weights 二、MLP Projection Weights:输入数据或隐藏层的输出投影到…

指针(1)-学习笔记

指针&#xff08;1&#xff09;-学习笔记 1.内存1.1内存 2.指针变量和地址2.1取地址操作符&#xff08;&&#xff09;2.2指针变量和解引用操作符&#xff08;*&#xff09;2.2.1 指针变量2.2.2解引用操作符 3 指针变量类型的意义3.1指针的解引用3.2指针-整数3.3 void*指针 …

PyTorch深度学习实战(7)—— 线性回归

线性回归是机器学习的入门内容&#xff0c;应用十分广泛。线性回归利用数理统计中的回归分析来确定两种或两种以上变量间相互依赖的定量关系&#xff0c;其表达形式为$y wxbe$。其中&#xff0c;$x$和$y$是输入输出数据&#xff0c;$w$和$b$是可学习参数&#xff0c;误差$e$服…

Pod的调度机制

文章目录 一、Pod调度概述二、Pod调度策略实现方式三、kube-scheduler调度1、kube-scheduler调度的流程2、过滤阶段3、打分阶段4、kube-scheduler 调度示例4.1、创建 Deployment 资源清单4.2、应用Deployment4.3、查看被kube-scheduler自动调度的Pod 四、nodeName调度1、创建Po…

MySQL操作2——表的的操作(增删查改)

创建表; 样例&#xff1a; 注意不同的存储引擎创建出来的表在目录下的个数不同 显示此数据库下的所有表&#xff1a;show tables; show create table name \G ——查看创建表时的具体信息 查看某个表的结构&#xff1a;desc 表名&#xff1b; 查看表中储存的数据&…

阿里云与优酷联袂:Create@AI江湖创作大赛,探索AI创新边界

随着网剧《少年白马醉春风》的热播&#xff0c;许多人心中的江湖梦被唤醒&#xff0c;渴望踏入那个充满传奇色彩的影视世界&#xff0c;体验一段属于自己的江湖之旅。在 AIGC 技术日益成熟的今天&#xff0c;这一梦想变得触手可及。阿里云携手优酷&#xff0c;发起了 Create A…

Hadoop YARN:现代大数据集群资源管理与作业调度

1.Yarn的概述 1.1.解释Yarn的定义和基本概念 Hadoop YARN&#xff08;Yet Another Resource Negotiator&#xff09;是 Hadoop 2.x 版本引入的一种资源管理器&#xff0c;用于管理和调度大数据集群中的资源&#xff0c;是 Hadoop 集群的核心组件之一。YARN 的设计目标是提高 H…

浅谈C语言动态内存分配

1、什么是动态内存分配 正常情况下&#xff0c;我们创建变量&#xff0c;都是向计算机内存中申请一个静态的内存&#xff0c;也就是说&#xff0c;一旦申请成功&#xff0c;这块内存的大小便不能再改变&#xff0c;并且能申请多少内存在一定程度上都是确定的。比如说&#xff…

【网络层】路由基础

文章目录 技术背景IP 路由工作原理IP路由表 技术背景 路由是能够让整个Internet持续运转的关键。回看之前的网络历史&#xff0c;最开始用一根网线将两台设备连接起来&#xff0c;面对面就能通信。到后面出现了交换机&#xff0c;能够让区域内的所有设备互相通信&#xff0c;形…

WPF篇(5)- Border控件(边框布局)+GridSplitter分割窗口

严格来说&#xff0c;Border并不是一个布局控件&#xff0c;因为它并不是Panel的子类&#xff0c;而是Decorator装饰器的子类&#xff0c;而Decorator继承于FrameworkElement。我们要先看看它的父类Decorator。 public class Decorator : FrameworkElement, IAddChild {public…

少儿编程 2024年6月scratch四级 电子学会图形化编程等级考试四级真题和答案解析(选择题)

2024年6月scratch编程等级考试四级真题 选择题&#xff08;共10题&#xff0c;每题2分&#xff0c;共20分&#xff09; 1、运行下列程序&#xff0c;输入单词“PLAY”最后角色说 A、LY4AP B、AP4LY C、YA4PL D、PL4AY 答案&#xff1a;B 考点分析&#xff1a;考查积木综合…

英伟达元宇宙平台Omniverse的学习,技术调研

NVIDIA Omniverse™ 是一个基于 USD (Universal Scene Description) 的可扩展平台&#xff0c;可使个人和团队更快地构建自定义 3D 工作流并模拟大型虚拟世界。 Omniverse&#xff1a;三维设计协同、模拟的开发平台&#xff0c;实现3D实时渲染&#xff0c;RTX光线追踪技术 协…

顺序表各种接口的实现(C)

线性表 线性表是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串…线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直线。在物理结构上并不一定是连续的&#xff0c;线性表在物…

Qt 小功能:加载等待动画——转圈圈

加载等待动画实现——转圈圈 效果图&#xff1a;&#xff08;看封面最好&#xff09; 关键要点 流畅的动画&#xff1a; 使用 QTimer 每 50 毫秒更新一次动画&#xff0c;确保动画流畅。 视觉效果&#xff1a; 使用 QPainter 的平滑像素转换和抗锯齿选项&#xff0c;提高动画…