spring cloud gateway 实现redis动态路由及自动项目路由上报
前言
spring cloud gateway默认为内存存储策略,通过配置文件加载的方式生成路由定义信息


可以看到,RouteDefinitionRepository继承了两个父接口,分别为RouteDefinitionLocator和RouteDefinitionWriter,RouteDefinitionLocator定义了路由定义获取接口,而RouteDefinitionWriter则定义了路由定义保存更新save接口和删除接口。
可以看到RouteDefinitionRepository是核心接口,其默认的实现类有上图两个,分别是基于内存和基于redis的,如果需要redis实现动态路由则只需要实现RouteDefinitionRepository接口并注入bean即可
一、实现RouteDefinitionRepository接口
这里其实spring cloud gateway已经有默认实现了的基于redis 的路由定义相关的repository,但由于其并没有做本地缓存,是纯redis的方式读取写入,这样会导致在进行路由调用的时候频繁的调用其getRouteDefinitions方法,频繁的读取redis其实也是有一定的性能损耗,因此我们其实稍加利用,添加本地缓存即可(本示例使用咖啡因缓存),注意添加注解:@EnableCaching
/*** @classDesc:* @author: cyjer* @date: 2023/1/30 9:53*/
@Component
@Slf4j
public class RedisRouteRepository implements RouteDefinitionRepository {private static final String ROUTE_DEFINITION_REDIS_KEY_PREFIX_QUERY = "routeDefinition::";private final ReactiveRedisTemplate<String, RouteDefinition> reactiveRedisTemplate;private final ReactiveValueOperations<String, RouteDefinition> routeDefinitionReactiveValueOperations;public RedisRouteRepository(ReactiveRedisTemplate<String, RouteDefinition> reactiveRedisTemplate) {this.reactiveRedisTemplate = reactiveRedisTemplate;this.routeDefinitionReactiveValueOperations = reactiveRedisTemplate.opsForValue();}@Override@Cacheable(cacheNames = "redisRoute")public Flux<RouteDefinition> getRouteDefinitions() {log.info("<<<<<<<<<<获取路由信息>>>>>>>>>>");return this.reactiveRedisTemplate.keys(this.createKey("*")).flatMap((key) -> {return this.reactiveRedisTemplate.opsForValue().get(key);}).onErrorContinue((throwable, routeDefinition) -> {if (log.isErrorEnabled()) {log.error("get routes from redis error cause : {}", throwable.toString(), throwable);}});}@Override@CacheEvict(cacheNames = "redisRoute")public Mono<Void> save(Mono<RouteDefinition> route) {log.info("<<<<<<<<<<保存路由信息>>>>>>>>>>");return route.flatMap((routeDefinition) -> {return this.routeDefinitionReactiveValueOperations.set(this.createKey(routeDefinition.getId()), routeDefinition).flatMap((success) -> {return success ? Mono.empty() : Mono.defer(() -> {return Mono.error(new RuntimeException(String.format("Could not add route to redis repository: %s", routeDefinition)));});});});}@Override@CacheEvict(cacheNames = "redisRoute")public Mono<Void> delete(Mono<String> routeId) {log.info("<<<<<<<<<<删除路由信息>>>>>>>>>>");return routeId.flatMap((id) -> {return this.routeDefinitionReactiveValueOperations.delete(this.createKey(id)).flatMap((success) -> {return success ? Mono.empty() : Mono.defer(() -> {return Mono.error(new NotFoundException(String.format("Could not remove route from redis repository with id: %s", routeId)));});});});}private String createKey(String routeId) {return ROUTE_DEFINITION_REDIS_KEY_PREFIX_QUERY + routeId;}}
/*** @classDesc: 本地缓存* @author: cyjer* @date: 2023/1/30 9:53*/
@Configuration
public class CaffeineConfig {@Bean@Primarypublic CacheManager caffeineCacheManager() {SimpleCacheManager cacheManager = new SimpleCacheManager();List<CaffeineCache> caches = new ArrayList<>();Map<String, Object> map = getCacheType();for (String name : map.keySet()) {caches.add(new CaffeineCache(name, (Cache<Object, Object>) map.get(name)));}cacheManager.setCaches(caches);return cacheManager;}/*** 初始化自定义缓存策略** @return*/private static Map<String, Object> getCacheType() {Map<String, Object> map = new ConcurrentHashMap<>();map.put("redisRoute", Caffeine.newBuilder().build());return map;}
}
到此其实基于redis的路由仓储service已经结束
基于zookeeper注册中心实现项目自动上报路由
为了实现动态路由,其实可以采用提供暴露接口的方式把相应的仓储接口提供出来,本示例提供的只是使用zk的监听机制,实现动态路由
关于zk的事件监听可以查看我的这篇文章:zookeeper相关操作
1、网关启动时添加zk监听事件
@Component
@Slf4j
@RequiredArgsConstructor
public class GatewayApplication implements ApplicationListener<ContextRefreshedEvent> {private final RouteDefinitionService routeDefinitionService;private final ZookeeperService zookeeperService;private final RouteProcesser routeProcesser;@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {//拉取网关配置routeDefinitionService.refreshRouteDefinition();log.info("<<<<<<<<<<刷新网关配置完成>>>>>>>>>>");zookeeperService.create(Constraint.ROUTE_DEFINITION, "init");zookeeperService.addWatchChildListener(Constraint.ROUTE_DEFINITION, routeProcesser);log.info("<<<<<<<<<<动态路由监听器配置完成>>>>>>>>>>");}
}
2、发布刷新事件:
public void refreshRouteDefinition() {this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));redisRouteRepository.getRouteDefinitions();}
3、 监听处理
/*** @classDesc: * @author: cyjer* @date: 2023/2/10 11:13*/
@Slf4j
@Component
@RequiredArgsConstructor
public class RouteProcesser extends AbstractChildListenerProcess implements ApiDefinitionConstraint {private static final String JTR = "/";private final static String ROUTE_CONFIG_ARGS_PREFIX = "_genkey_";private final RedisRouteRepository redisRouteRepository;private final RouteDefinitionService routeDefinitionService;@Overridepublic void process(CuratorFramework curatorFramework, PathChildrenCacheEvent cacheEvent) {ChildData data = cacheEvent.getData();if (Objects.nonNull(data) && cacheEvent.getType().equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)) {log.info("<<<<<<<<<<监听到动态路由变动申请>>>>>>>>>>");String content = new String(data.getData(), StandardCharsets.UTF_8);SiriusRouteDefinition siriusRouteDefinition = JSONObject.parseObject(content, SiriusRouteDefinition.class);RouteDefinition routeDefinition = new RouteDefinition();String serviceId = siriusRouteDefinition.getServiceId();routeDefinition.setId(serviceId);URI routeUri = UriComponentsBuilder.fromUriString("lb://" + serviceId).build().toUri();routeDefinition.setUri(routeUri);List<PredicateDefinition> predicates = getPredicates(JTR + serviceId + "/**");if (!predicates.isEmpty()) {routeDefinition.setPredicates(predicates);}// 获取过滤器List<FilterDefinition> filters = new ArrayList<>();Map<String, String> args = new LinkedHashMap<>();args.put(ROUTE_CONFIG_ARGS_PREFIX, "1");FilterDefinition filterDefinition = new FilterDefinition();filterDefinition.setName("StripPrefix");filterDefinition.setArgs(args);filters.add(filterDefinition);routeDefinition.setFilters(filters);redisRouteRepository.save(Mono.just(routeDefinition)).subscribe();routeDefinitionService.refreshRouteDefinition();log.info("<<<<<<<<<<刷新网关路由成功>>>>>>>>>>");}}/*** 获取地址断言** @param routePaths 路由地址* @return java.util.List*/private List<PredicateDefinition> getPredicates(String routePaths) {Map<String, String> args = new HashMap<>();String[] paths = routePaths.split(",");for (int i = 0; i < paths.length; i++) {args.put(ROUTE_CONFIG_ARGS_PREFIX + i, paths[i]);}PredicateDefinition predicate = new PredicateDefinition();predicate.setName("Path");predicate.setArgs(args);List<PredicateDefinition> predicates = new ArrayList<>();predicates.add(predicate);return predicates;}}
4、非网关的其他项目启动时注册路由
/*** @classDesc: 注册应用路由信息* @author: cyjer* @date: 2023/2/11 20:47*/
@Component
@Order(2)
@RequiredArgsConstructor
@Slf4j
public class ServiceRouteDefinitionReporter implements CommandLineRunner, Constraint {private final GatewayServiceProperties gatewayServiceProperties;private final ZookeeperService zookeeperService;@Overridepublic void run(String... args) throws Exception {String applicationName = apiDefinitionReporter.getApplicationName();siriusRouteDefinition.setServiceId(applicationName);siriusRouteDefinition.setGatewayId(gatewayServiceProperties.getGatewayId());zookeeperService.create(ROUTE_DEFINITION + SPLIT + applicationName, JSONObject.toJSONString(siriusRouteDefinition));zookeeperService.update(ROUTE_DEFINITION + SPLIT + applicationName, JSONObject.toJSONString(siriusRouteDefinition));log.info("<<<<< successfully reported route information >>>>>");}}
相关文章:
spring cloud gateway 实现redis动态路由及自动项目路由上报
前言 spring cloud gateway默认为内存存储策略,通过配置文件加载的方式生成路由定义信息 可以看到,RouteDefinitionRepository继承了两个父接口,分别为RouteDefinitionLocator和RouteDefinitionWriter,RouteDefinitionLocator定…...
c++函数对象(仿函数)、谓词、内建函数对象
1、函数对象 1.1 概念 重载函数调用操作符的类,这个类的对象就是函数对象,在使用这个函数对象对应使用重载的()符号时,行为类似于函数调用,因此这个函数也叫仿函数。 注意:函数对象࿰…...
物联网对供应链管理的影响
物联网对于许多行业来说都是一项革命性技术,其应用领域涉及零售、交通、金融、医疗保健和能源等行业。物联网在供应链等流程中已经展示了其深度的潜力。管理、预测和监督应用程序有助于车队运输经理提高配送的运营效率,并增加决策的准确性。如今…...
c++ 那些事 笔记
GitHub - Light-City/CPlusPlusThings: C那些事 1. ① extern extern关键字,C语言extern关键字用法详解 如果全局变量不在文件的开头定义,其有效的作用范围只限于其定义处到文件结束。如果在定义点之前的函数想引用该全局变量,则应该在…...
心跳机制Redis
进入命令传播阶段候,master与slave间需要进行信息交换,使用心跳机制进行维护,实现双方连接保持在线 master心跳: 指令:PING 周期:由repl-ping-slave-period决定,默认10秒 作用&#…...
蓝桥杯算法训练合集十七 1.数字反转2.试题39713.矮人采金子4.筛法5.机器指令
目录 1.数字反转 2.试题3971 3.矮人采金子 4.筛法 5.机器指令 1.数字反转 问题描述 给定一个整数,请将该数各个位上数字反转得到一个新数。新数也应满足整数的常见形式,即除非给定的原数为零,否则反转后得到的新数的最高位数字不应为零&…...
第一章 初识 Spring Security
第一章 初识 Spring Security 1、权限管理 权限管理 基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现了对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资…...
2023-02-20 关于回朔的思考
摘要: 考虑命运来回动荡交织,一些新的规划在不断的扩充, 而一些历史则开始陷入回朔。 有必要对历史和过往做一些规划和思考。 需要注意在这个阶段, 第一优先级是在反刍中将其最大化。 理论层: 一. 数据库的基础理论 ANSI SQL到词法解析和语法解析mysql的SQL层对…...
推荐系统[八]算法实践总结V1:淘宝逛逛and阿里飞猪个性化推荐:召回算法实践总结【冷启动召回、复购召回、用户行为召回等算法实战】
0.前言:召回排序流程策略算法简介 推荐可分为以下四个流程,分别是召回、粗排、精排以及重排: 召回是源头,在某种意义上决定着整个推荐的天花板;粗排是初筛,一般不会上复杂模型;精排是整个推荐环节的重中之重,在特征和模型上都会做的比较复杂;重排,一般是做打散或满足…...
适合初学者的超详细实用调试技巧(下)
我们日常写代码的时候,常常会遇到bug的情况,这个时候像我这样的初学者就会像无头苍蝇一样这里改改那里删删,调试的重要性也就显现出来,这篇文章接着上文来讲解。 上文地址:(8条消息) 适合初学者的超详细实用调试技巧&…...
C# String与StringBuilder 的区分
重点 1)它是比较的栈里面的值是否相等(值比较) 2)Equals它比较的是堆里面的值是否相等(引用地址值比较) 3)Object.ReferenceEquals(obj1,obj2)它是比较的是内存地址是否相等 问题描述: 今日提交代码时候,被检测工具发出修改建议。遂补充一下知识 1.什么…...
【麒麟】基于GPS北斗卫星技术的NTP网络时间服务器
【麒麟】基于GPS北斗卫星技术的NTP网络时间服务器 【麒麟】基于GPS北斗卫星技术的NTP网络时间服务器 麒麟系统NTP授时方案 设计思路: 在通用的麒麟服务器内部固定一块北斗卫星接收模块并引出卫星天线接口,卫星模块接收北斗卫星数据并解码输出时间数据&…...
“互联网+”下劳动关系认定的现状
1. 劳动关系的认定标准。依据目前我国法律的有关规定, 判定劳动关系存在两种情况:其一, 在有书面劳动合同的情况下, 这时应以书面合同作为认定标准;其二, 在没有书面合同的情况下, 则依据2005年劳社部的《关于确立劳动关系有关事项的通知》来认定, 其中第一条:“用人单位招用劳…...
LPWAN及高效弹性工业物联网核心技术方案
20多年前的一辆拖拉机就是一个纯机械的产品,里面可能并没有电子或者软件的构成;而随后随着软件的发展,拖拉机中嵌入了软件,它能控制发动机的功率及拖拉机防抱死系统;接下来,通过融入各种软件,拖…...
OPTIONS FMTSEARCH
FMTSEARCH 指定要检索的格式目录列表,语法如下:OPTIONS FMTSEARCH(catalog-specification-1<catalog-specification-2 … >);使用PROC FORMAT时可以定义格式目录,LIBRARYlibref或LIBRARYlibref.catalog。格式目录可以是libref或libref.…...
Python3 pip
Python3 pip pip 是 Python 包管理工具,该工具提供了对 Python 包的查找、下载、安装、卸载的功能。 软件包也可以在 https://pypi.org/ 中找到。 目前最新的 Python 版本已经预装了 pip。 注意:Python 2.7.9 或 Python 3.4 以上版本都自带 pip 工具…...
【2023-02-20】JS逆向之翼支付
提示:文章仅供参考,禁止用于非法途径 文章目录前言分析总结前言 真的好久没更了…… 提示:以下是本篇文章正文内容,下面案例可供参考 分析 进到网页,加载两个接口 applyLoginFactor 接口返回一个RSA公钥࿰…...
假如面试官问你Babel的原理该怎么回答
1. 什么是 Babel 简单地说,Babel 能够转译 ECMAScript 2015 的代码,使它在旧的浏览器或者环境中也能够运行。 // es2015 的 const 和 arrow function const add (a, b) > a b;// Babel 转译后 var add function add(a, b) {return a b; };Babel…...
深入Spring底层透析Bean创建过程之拨云见日篇
目录前言一.BeanFactory快速入门1. BeanFactory创建Bean2. BeanFactory和ApplicationContext的关系3. 和ApplicationContext区别(高频问点)4. BeanFactory的继承体系5. ApplicationContext的继承体系二.Bean实例化的基本流程(重点)前言 首先感谢您的阅览࿰…...
8 狗监控的封装
概述 为了保证嵌入式程序能够长时间稳定地运行,需要加入狗监控机制。狗监控的原理为:应用程序需要每隔一段时间来喂狗或保活,如果应用程序崩溃或者内核崩溃,导致长时间无法喂狗,则狗将超时,会自动重启系统。部分IPC芯片提供了硬件狗,对于没有硬件狗的,需要自行实现软件…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...
链式法则中 复合函数的推导路径 多变量“信息传递路径”
非常好,我们将之前关于偏导数链式法则中不能“约掉”偏导符号的问题,统一使用 二重复合函数: z f ( u ( x , y ) , v ( x , y ) ) \boxed{z f(u(x,y),\ v(x,y))} zf(u(x,y), v(x,y)) 来全面说明。我们会展示其全微分形式(偏导…...
EEG-fNIRS联合成像在跨频率耦合研究中的创新应用
摘要 神经影像技术对医学科学产生了深远的影响,推动了许多神经系统疾病研究的进展并改善了其诊断方法。在此背景下,基于神经血管耦合现象的多模态神经影像方法,通过融合各自优势来提供有关大脑皮层神经活动的互补信息。在这里,本研…...
CppCon 2015 学习:Simple, Extensible Pattern Matching in C++14
什么是 Pattern Matching(模式匹配) ❝ 模式匹配就是一种“描述式”的写法,不需要你手动判断、提取数据,而是直接描述你希望的数据结构是什么样子,系统自动判断并提取。❞ 你给的定义拆解: ✴ Instead of …...
