SpringCloudGateway 动态转发后端服务
API网关的核心功能是统一流量入口,实现路由转发,SpringCloudGateway是API网关开发的技术之一,此外比较流行的还有Kong和ApiSix,这2个都是基于OpenResty技术栈。
简单的路由转发可以通过SpringCloudGateway的配置文件实现,在一些业务场景种,会需要动态替换路由配置中的后端服务地址,单纯靠配置文件无法满足这种需求。
本文介绍一种将路由配置保存到数据库中,可以根据接口请求的特定条件,从数据库中动态读取后端服务地址,实现灵活转发。
具体的代码参照 示例项目 https://github.com/qihaiyan/springcamp/tree/master/spring-cloud-gateway
一、概述
通过把SpringCloudGateway的相关路由配置规则保存到数据库中,可以动态的灵活调整路由。在本文的实现中,我们通过请求header中的特定值,动态选择对应的后端服务地址。
二、项目中加入依赖
在项目的gradle中增加依赖关系。
build.gradle:
plugins {id 'org.springframework.boot' version '3.0.2'id 'io.spring.dependency-management' version '1.1.0'id 'java'
}group = 'cn.springcamp'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'configurations {compileOnly {extendsFrom annotationProcessor}testCompileOnly {extendsFrom testAnnotationProcessor}
}repositories {mavenCentral()
}dependencies {implementation "org.springframework.boot:spring-boot-starter-json"implementation 'org.springframework.boot:spring-boot-starter-validation'implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'implementation 'org.springframework.cloud:spring-cloud-starter-gateway'runtimeOnly 'com.h2database:h2'runtimeOnly 'io.r2dbc:r2dbc-h2'annotationProcessor 'org.projectlombok:lombok'testAnnotationProcessor 'org.projectlombok:lombok'testImplementation "org.springframework.boot:spring-boot-starter-test"testImplementation 'org.junit.vintage:junit-vintage-engine'testImplementation 'io.projectreactor:reactor-test'testImplementation 'com.h2database:h2'testImplementation 'io.r2dbc:r2dbc-h2'testImplementation 'org.junit.vintage:junit-vintage-engine'
}dependencyManagement {imports {mavenBom "org.springframework.cloud:spring-cloud-dependencies:2022.0.1"}
}test {useJUnitPlatform()
}
由于SpringCloudGateway基于SpringWebFlux技术构建,所以依赖中的数据库配置需要使用r2dbc 。
三、配置文件
示例程序首选通过配置文件对路由进行基本配置,配置文件代码:
spring:r2dbc:url: r2dbc:h2:mem:///testdb?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSEusername: sapassword:cloud:gateway:routes:- id: routeOnepredicates:- Path=/route1/**uri: no://opfilters:- UriHostPlaceholderFilter=10001- id: routeTwopredicates:- Path=/route2/**uri: no://opfilters:- UriHostPlaceholderFilter=10001
配置文件中配置了2个路由,对应的接口地址路径分别是 /route1/**和 Path=/route2/**,路径中的 ***表示模糊匹配,只要是以 /route1/为前缀的路径都可以被访问到。
后端服务地址配置了一个无意的地址: uri: no://op,因为我们的处理逻辑会通过从数据库中读取配置来动态替换后端服务地址。
三、动态路由数据存储格式
我们通过 ROUTE_FILTER_ENTITY这个数据库表来存储接口后端服务配置数据。表结构为:
CREATE TABLE "ROUTE_FILTER_ENTITY"
(id VARCHAR(255) PRIMARY KEY,route_id VARCHAR(255), -- 路由ID,对应配置文件中的 ```id```配置项code VARCHAR(255), -- 接口请求header中的code参数的值url VARCHAR(255) -- 后端服务地址
);
当客户端访问 /route1/test接口时,根据配置文件的路由配置,SpringCloudGateway 会命中 id: routeOne这个路由规则,这个规则对应的后端服务地址是 uri: no://op,并不是我们期望的真实后端服务地址。
因此,我们需要读取到真实的后端服务地址,并将请求转发到这个地址。跟据 routeId 和 接口请求header中的code参数的值,就可以从 ROUTE_FILTER_ENTITY 表中查到对应的后端服务地址 url这个字段的值。
我们已经读取到了后端服务地址,还需要将请求转发到这个地址,下面介绍转发的方法。
四、后端服务动态转发
动态转发通过自定义 filter 的方式实现,自定义 filter 代码如下:
@Component
public class UriHostPlaceholderFilter extends AbstractGatewayFilterFactory<UriHostPlaceholderFilter.Config> {@Autowiredprivate RouteFilterRepository routeFilterRepository;public UriHostPlaceholderFilter() {super(Config.class);}@Overridepublic List<String> shortcutFieldOrder() {return Collections.singletonList("order");}@Overridepublic GatewayFilter apply(Config config) {return new OrderedGatewayFilter((exchange, chain) -> {String code = exchange.getRequest().getHeaders().getOrDefault("code", new ArrayList<>()).stream().findFirst().orElse("");String routeId = exchange.getAttribute(GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR);if (StringUtils.hasText(code)) {String newurl;try {newurl = routeFilterRepository.findByRouteIdAndCode(routeId, code).toFuture().get().getUrl();} catch (InterruptedException | ExecutionException e) {throw new RuntimeException(e);}if (StringUtils.hasText(exchange.getRequest().getURI().getQuery())) {newurl = newurl + "?" + exchange.getRequest().getURI().getQuery();}URI newUri = null;try {newUri = new URI(newurl);} catch (URISyntaxException e) {log.error("uri error", e);}exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newUri);}return chain.filter(exchange);}, config.getOrder());}@Data@NoArgsConstructorpublic static class Config {private int order;public Config(int order) {this.order = order;}}
}
通过扩展 AbstractGatewayFilterFactory 类,我们自定义了 UriHostPlaceholderFilter 这个 filter 。
代码的核心逻辑在 apply 方法中。
首先通过 String code = exchange.getRequest().getHeaders().getOrDefault("code", new ArrayList<>()).stream().findFirst().orElse("")可以获取到接口请求 header 中 code 这个参数的值。
再通过 String routeId = exchange.getAttribute(GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR)可以获取到 routeId 。
最后通过 newurl = routeFilterRepository.findByRouteIdAndCode(routeId, code).toFuture().get().getUrl()就可以从数据库中读取到配置好的后端服务地址。
拿到后端服务地址后, 通过调用 exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newUri);将请求转发到对应的地址。
四、单元测试
在单元测试代码中,我们预置了一条后端服务动态配置数据:
insert into ROUTE_FILTER_ENTITY values('1','routeOne','alpha','http://httpbin.org/anything')
然后模拟请求 /route1/test?a=test这个接口,根据我们的配置,请求会被转发到 http://httpbin.org/anything。
执行单元测试后,可以从日志中发现,接口返回的数据是 http://httpbin.org/anything 这个后端服务返回的数据。
当我们希望调整后端服务地址时,只需要把 ROUTE_FILTER_ENTITY 表中的这条配置数据中的 url 字段改成其它的任何服务地址即可,大大增加了程序的灵活度。
相关文章:
SpringCloudGateway 动态转发后端服务
API网关的核心功能是统一流量入口,实现路由转发,SpringCloudGateway是API网关开发的技术之一,此外比较流行的还有Kong和ApiSix,这2个都是基于OpenResty技术栈。 简单的路由转发可以通过SpringCloudGateway的配置文件实现…...
使用canvas写一个flappy bird小游戏
简介 canvas 是HTML5 提供的一种新标签,它可以支持 JavaScript 在上面绘画,控制每一个像素,它经常被用来制作小游戏,接下来我将用它来模仿制作一款叫flappy bird的小游戏。flappy bird(中文名:笨鸟先飞&am…...
KVM-2、虚拟化基础
1. 虚拟化概念 什么是虚拟化 **虚拟化是使用所谓虚拟机管理程序从一台物理机上创建若干个虚拟机的过程。**虚拟机的行为和运转方式与物理机一样,但它们会使用物理机的计算资源,如 CPU 、内存和存储。虚拟机管理程序会根据需要将这些计算资源分配给每个虚拟机。 虚拟化有哪…...
设计模式之观察者模式与访问者模式详解和应用
目录1.访问者模式详解1.1 访问者模式的定义1.1.1 访问者模式在生活中的体现1.1.2 访问者模式的适用场景1.2 访问者模式的通用实现1.3 访问者模式的使用案例之KPI考核1.3.1 类图设计1.3.2 代码实现1.4 访问者模式扩展---分派1.4.1 java中静态分派示例代码1.4.2 java中动态分派1.…...
spring注解方式整合Dubbo源码解析
系列文章目录 前言 本节我们的Dubbo源码版本基于2.6.x 在前一章我们的整合案例中,我们有几个比较关键的步骤: 在启动类上标注了EnableDubbo注解在provider类上面标注了Service注解来提供dubbo服务在消费的时候通过Reference注解引入dubbo服务在配置文件…...
大数值金额大写转换(C语言)
关于大数值金额大写转换,在财务管理的应用方面没什么意义。一般来说,千亿级,万亿级的数值就够了。因为在国家级层面是以亿为单位的,也就表达为千万亿,万万亿。在企业层面数值金额转换设置到千亿、万亿就行了。大的集团…...
迷宫问题图解 : 基于骨架提取、四邻域
目录 1. 迷宫的连通域 2. How to remove branch ? 3. 基于4邻域的 remove 分支 3.1 找到分支的端点 3.2 4邻域的 remove 分支 3.3 循环移除分支 3.4 code 4. 迷宫路线 4.1 预处理 4.2 提取骨架 4.3 分支的端点 4.4 去除分支的端点 4.5 循环去除分支 4…...
设计模式 - 如何在库和主程序之间互相调用数据和函数
背景:在项目开发过程中,难免碰到这种情况,当我们想要通过我们开发的库,调用主程序中的一些变量或者函数的时候,就会导致一些问题,因为在项目构建过程中,库都是不依赖于主程序编译的,…...
Redis面试题:1~2亿条数据需要缓存,请问如何设计这个存储案例
目录 前言 一、哈希取余分区 优点 缺点 二、一致性哈希算法分区 背景 步骤 ① 算法构建一致性哈希环 ② 服务器IP节点映射 ③ key落到服务器的落键规则 优点 ① 容错性 ② 扩展性 缺点 三、哈希槽分区 前言 单机单台100%不可能,肯定是分布式存储&am…...
程序员必备的软技能-《如何阅读一本书》
阅读很重要,我们真的会阅读吗? 这本书的初版是 1940年,时隔 80年,其内容仍然不过时。第一次读这本书时,给我最大的影响就是主题阅读,每次学习一个新理论、技术,都入手多本关于这项理论、技术的书…...
Java数据结构-栈、队列常用类(Stack、ArrayDeque、LinkedLList)
数据结构的三要素包括:逻辑结构、存储结构、数据的运算。逻辑结构描述的是数据之间的逻辑关系,分为线性结构(线性表(数组、链表)、栈、队列)和非线性结构(图、树、集合)。物理结构也…...
拯救了大批爬虫程序员,因为一个简单的神器
相信大家应该都写过爬虫,简单的爬虫只需要使用 requests 即可。遇到复杂的爬虫,就需要在程序里面加上请求头和参数信息。类似这种:我们一般的步骤是,先到浏览器的网络请求中找到我们需要的请求,然后将请求头和参数信息…...
2023年美赛C题Wordle预测问题三、四建模及Python代码详细讲解
更新时间:2023-2-19 16:30 相关链接 (1)2023年美赛C题Wordle预测问题一建模及Python代码详细讲解 (2)2023年美赛C题Wordle预测问题二建模及Python代码详细讲解 (3)2023年美赛C题Wordle预测问题三、四建模…...
相关性-回忆录(持续更新)
1.TODO方向 (1)数据增强:finetuning阶段需要大量人工标注样本,消耗时间和成本。用户点击数据作为弱监督学习,可以尝试图网络构建节点和边(query聚合); 使用展现未点击生成对抗网络进…...
(必备技能)使用Python实现屏幕截图
(必备技能)使用Python实现屏幕截图 文章目录 (必备技能)使用Python实现屏幕截图 一、序言二、环境配置 1、下载pyautogui包2、下载opencv-python包3、下载PyQt5包4、下载pypiwin32包 三、屏幕截屏源码与解析 1、使用pyautogui方法实现截屏2、使用PyQt方法实现截屏 a.获取窗口…...
「数据仓库」怎么选择现代数据仓库?
构建自己的数据仓库时要考虑的基本因素我们用过很多数据仓库。当我们的客户问我们,对于他们成长中的公司来说,最好的数据仓库是什么时,我们会根据他们的具体需求来考虑答案。通常,他们需要几乎实时的数据,价格低廉&…...
6.3 使用 Swagger 生成 Web API 文档
第6章 构建 RESTful 服务 6.1 RESTful 简介 6.2 构建 RESTful 应用接口 6.3 使用 Swagger 生成 Web API 文档 6.4 实战:实现 Web API 版本控制 6.3 使用 Swagger 生成 Web API 文档 高质量的 API 文档在系统开发的过程中非常重要。本节介绍什么是 Swaggerÿ…...
Day894.加锁规则的一些问题 -MySQL实战
加锁规则的一些问题 Hi,我是阿昌,今天学习记录的是关于加锁规则的一些问题的内容。 加锁规则,这个规则中,包含了两个“原则”、两个“优化”和一个“bug”: 原则 1:加锁的基本单位是 next-key lock。nex…...
【Flutter入门到进阶】Dart进阶篇---Dart异步编程
1 并行与并发的编程区别 1.1 并发与并行 1.1.1 说明 我们举个例子,如果有条高速公路 A 上面并排有 8 条车道,那么最大的并行车辆就是 8 辆此条高速公路 A 同时并排行走的车辆小于等于 8 辆的时候,车辆就可以并行运行。 CPU 也是这个原理,一个 CPU 相当于一个高速公路 A,核心数…...
可穿戴设备十年演进:从技术突破到健康与生产力工具
1. 从预言到现实:可穿戴计算浪潮的十年回望与深度拆解十年前,当EE Times那篇关于Apple iWatch和Google Glasses将引领可穿戴计算浪潮的文章发表时,业界还弥漫着一种将信将疑的氛围。彼时,智能手机正处巅峰,人们很难想象…...
终极指南:5步安装Koikatu HF Patch解锁完整游戏体验
终极指南:5步安装Koikatu HF Patch解锁完整游戏体验 【免费下载链接】KK-HF_Patch Automatically translate, uncensor and update Koikatu! and Koikatsu Party! 项目地址: https://gitcode.com/gh_mirrors/kk/KK-HF_Patch KK-HF Patch是专为《恋活…...
AI编程规划工具vibe-driven-dev:从模糊想法到清晰开发蓝图
1. 项目概述:从“感觉”到“计划”的桥梁在AI编程助手(或者说“编码智能体”)越来越普及的今天,一个常见的困境是:我们脑子里有一个很棒的产品想法,但当你试图把它交给Claude Code、Cursor或者Windsurf这类…...
一次搞清楚:Agent、Skill、Prompt、MCP
文章深入探讨了AI Agent在落地过程中面临的三大核心痛点:Prompt的临时性与不可复用性、Agent专业能力的难以沉淀与迁移、以及AI能力无法融入现有工程化流程。文章提出Agent Skills作为AI Agent的专业能力说明书,通过标准化能力描述与执行框架,…...
【Midjourney Gouache风格终极指南】:20年AI绘画专家亲授7大参数黄金组合与3类易踩翻车点
更多请点击: https://intelliparadigm.com 第一章:Gouache风格的本质解构与AI绘画语境迁移 Gouache(水粉画)并非简单意义上的“不透明水彩”,其本质在于颜料颗粒的物理遮盖性、媒介乳化稳定性与干湿叠压响应的三重耦合…...
资源受限场景下基于强化学习的自适应AI安全框架设计与实践
1. 项目概述:当AI安全遇上资源与伦理的双重挑战最近和几位在非洲做技术援助的朋友聊起他们的工作,他们提到一个很有意思的困境:在乌干达这样的地区,网络安全监测的需求日益增长,但本地计算资源极其有限,网络…...
告别ElementUI日历的默认样式!手把手教你用SCSS深度定制一个高颜值日历组件
从零打造高颜值日历组件:ElementUI Calendar深度定制指南 当你打开项目后台管理系统,那个灰扑扑的默认日历组件是否总让你皱眉?作为前端开发者,我们经常需要在不破坏原有功能的前提下,为ElementUI的Calendar组件换上符…...
工控人必备技能:VMware虚拟机+Win10+博途V15完整开发环境搭建实录(从镜像下载到PLC在线)
工控工程师的移动工作站:VMwareWin10博途V15全栈开发环境实战指南 在工业自动化领域,能够随时随地进行PLC程序开发和调试的能力已经成为工程师的核心竞争力。想象这样一个场景:深夜接到产线紧急故障通知,而你的开发环境却锁在办公…...
OpenClaw快速上手:从第一次对话到第一个自动化任务
OpenClaw快速上手:从第一次对话到第一个自动化任务 版本说明:本文基于OpenClaw 2026.3.2版本编写。该版本经过充分验证,稳定可靠,且预装了49个内置技能,本文的演示将主要依赖这些技能。 在OpenClaw的官方教程中&#x…...
魔兽争霸3终极优化指南:WarcraftHelper 2024免费配置教程
魔兽争霸3终极优化指南:WarcraftHelper 2024免费配置教程 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 还在为经典游戏《魔兽争霸3》在现…...
