当前位置: 首页 > news >正文

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网关的核心功能是统一流量入口&#xff0c;实现路由转发&#xff0c;SpringCloudGateway是API网关开发的技术之一&#xff0c;此外比较流行的还有Kong和ApiSix&#xff0c;这2个都是基于OpenResty技术栈。 简单的路由转发可以通过SpringCloudGateway的配置文件实现&#xf…...

使用canvas写一个flappy bird小游戏

简介 canvas 是HTML5 提供的一种新标签&#xff0c;它可以支持 JavaScript 在上面绘画&#xff0c;控制每一个像素&#xff0c;它经常被用来制作小游戏&#xff0c;接下来我将用它来模仿制作一款叫flappy bird的小游戏。flappy bird&#xff08;中文名&#xff1a;笨鸟先飞&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.…...

[SSD固态硬盘技术 18] Over-Provisioning (OP 预留空间)详解,谁“偷”走了SSD的容量?

</...

spring注解方式整合Dubbo源码解析

系列文章目录 前言 本节我们的Dubbo源码版本基于2.6.x 在前一章我们的整合案例中&#xff0c;我们有几个比较关键的步骤&#xff1a; 在启动类上标注了EnableDubbo注解在provider类上面标注了Service注解来提供dubbo服务在消费的时候通过Reference注解引入dubbo服务在配置文件…...

大数值金额大写转换(C语言)

关于大数值金额大写转换&#xff0c;在财务管理的应用方面没什么意义。一般来说&#xff0c;千亿级&#xff0c;万亿级的数值就够了。因为在国家级层面是以亿为单位的&#xff0c;也就表达为千万亿&#xff0c;万万亿。在企业层面数值金额转换设置到千亿、万亿就行了。大的集团…...

迷宫问题图解 : 基于骨架提取、四邻域

目录 1. 迷宫的连通域 2. How to remove branch &#xff1f; 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…...

设计模式 - 如何在库和主程序之间互相调用数据和函数

背景&#xff1a;在项目开发过程中&#xff0c;难免碰到这种情况&#xff0c;当我们想要通过我们开发的库&#xff0c;调用主程序中的一些变量或者函数的时候&#xff0c;就会导致一些问题&#xff0c;因为在项目构建过程中&#xff0c;库都是不依赖于主程序编译的&#xff0c;…...

Redis面试题:1~2亿条数据需要缓存,请问如何设计这个存储案例

目录 前言 一、哈希取余分区 优点 缺点 二、一致性哈希算法分区 背景 步骤 ① 算法构建一致性哈希环 ② 服务器IP节点映射 ③ key落到服务器的落键规则 优点 ① 容错性 ② 扩展性 缺点 三、哈希槽分区 前言 单机单台100%不可能&#xff0c;肯定是分布式存储&am…...

程序员必备的软技能-《如何阅读一本书》

阅读很重要&#xff0c;我们真的会阅读吗&#xff1f; 这本书的初版是 1940年&#xff0c;时隔 80年&#xff0c;其内容仍然不过时。第一次读这本书时&#xff0c;给我最大的影响就是主题阅读&#xff0c;每次学习一个新理论、技术&#xff0c;都入手多本关于这项理论、技术的书…...

Java数据结构-栈、队列常用类(Stack、ArrayDeque、LinkedLList)

数据结构的三要素包括&#xff1a;逻辑结构、存储结构、数据的运算。逻辑结构描述的是数据之间的逻辑关系&#xff0c;分为线性结构&#xff08;线性表&#xff08;数组、链表&#xff09;、栈、队列&#xff09;和非线性结构&#xff08;图、树、集合&#xff09;。物理结构也…...

拯救了大批爬虫程序员,因为一个简单的神器

相信大家应该都写过爬虫&#xff0c;简单的爬虫只需要使用 requests 即可。遇到复杂的爬虫&#xff0c;就需要在程序里面加上请求头和参数信息。类似这种&#xff1a;我们一般的步骤是&#xff0c;先到浏览器的网络请求中找到我们需要的请求&#xff0c;然后将请求头和参数信息…...

2023年美赛C题Wordle预测问题三、四建模及Python代码详细讲解

更新时间:2023-2-19 16:30 相关链接 &#xff08;1&#xff09;2023年美赛C题Wordle预测问题一建模及Python代码详细讲解 &#xff08;2&#xff09;2023年美赛C题Wordle预测问题二建模及Python代码详细讲解 &#xff08;3&#xff09;2023年美赛C题Wordle预测问题三、四建模…...

相关性-回忆录(持续更新)

1.TODO方向 &#xff08;1&#xff09;数据增强&#xff1a;finetuning阶段需要大量人工标注样本&#xff0c;消耗时间和成本。用户点击数据作为弱监督学习&#xff0c;可以尝试图网络构建节点和边&#xff08;query聚合&#xff09;&#xff1b; 使用展现未点击生成对抗网络进…...

(必备技能)使用Python实现屏幕截图

(必备技能)使用Python实现屏幕截图 文章目录 (必备技能)使用Python实现屏幕截图 一、序言二、环境配置 1、下载pyautogui包2、下载opencv-python包3、下载PyQt5包4、下载pypiwin32包 三、屏幕截屏源码与解析 1、使用pyautogui方法实现截屏2、使用PyQt方法实现截屏 a.获取窗口…...

「数据仓库」怎么选择现代数据仓库?

构建自己的数据仓库时要考虑的基本因素我们用过很多数据仓库。当我们的客户问我们&#xff0c;对于他们成长中的公司来说&#xff0c;最好的数据仓库是什么时&#xff0c;我们会根据他们的具体需求来考虑答案。通常&#xff0c;他们需要几乎实时的数据&#xff0c;价格低廉&…...

6.3 使用 Swagger 生成 Web API 文档

第6章 构建 RESTful 服务 6.1 RESTful 简介 6.2 构建 RESTful 应用接口 6.3 使用 Swagger 生成 Web API 文档 6.4 实战&#xff1a;实现 Web API 版本控制 6.3 使用 Swagger 生成 Web API 文档 高质量的 API 文档在系统开发的过程中非常重要。本节介绍什么是 Swagger&#xff…...

Day894.加锁规则的一些问题 -MySQL实战

加锁规则的一些问题 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于加锁规则的一些问题的内容。 加锁规则&#xff0c;这个规则中&#xff0c;包含了两个“原则”、两个“优化”和一个“bug”&#xff1a; 原则 1&#xff1a;加锁的基本单位是 next-key lock。nex…...

【Flutter入门到进阶】Dart进阶篇---Dart异步编程

1 并行与并发的编程区别 1.1 并发与并行 1.1.1 说明 我们举个例子,如果有条高速公路 A 上面并排有 8 条车道,那么最大的并行车辆就是 8 辆此条高速公路 A 同时并排行走的车辆小于等于 8 辆的时候,车辆就可以并行运行。 CPU 也是这个原理,一个 CPU 相当于一个高速公路 A,核心数…...

stm32G473的flash模式是单bank还是双bank?

今天突然有人stm32G473的flash模式是单bank还是双bank&#xff1f;由于时间太久&#xff0c;我真忘记了。搜搜发现&#xff0c;还真有人和我一样。见下面的链接&#xff1a;https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

Linux nano命令的基本使用

参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时&#xff0c;显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...

华为OD机试-最短木板长度-二分法(A卷,100分)

此题是一个最大化最小值的典型例题&#xff0c; 因为搜索范围是有界的&#xff0c;上界最大木板长度补充的全部木料长度&#xff0c;下界最小木板长度&#xff1b; 即left0,right10^6; 我们可以设置一个候选值x(mid)&#xff0c;将木板的长度全部都补充到x&#xff0c;如果成功…...