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

通过SpringCloud Gateway实现API接口镜像请求(陪跑)网关功能

前言

在某些业务场景中,为实现新老接口的顺利切换,需要设置新接口陪同老接口同时运行(即接口陪跑)、比较新老接口的执行结果,待比较验证通过后,再正式切换到新接口。

我们可将这种在调用老接口的同时调用新接口进行陪跑的功能,称为”接口镜像“。并且可以通过 Spring Cloud Gateway实现接口镜像。

Spring Cloud Gateway概述

目前Spring Cloud的 API网关,主要基于Spring 6、Spring Boot 3,可提供API路由及安全、监控统计和可扩展性等网关基本功能。其中有两个核心概念:Server 和Proxy exchange代理交换机,可兼容WebFlux 和 MVC两种模式。

网关提供了很多内置的Gateway Filter工厂类,提供了多种路由过滤器,用于针对特定路由进行修改HTTP请求或响应。同时,我们可以自定义GatewayFilterFactory,从而实现更强大的定制化功能。

详细介绍,参见:Spring Cloud Gateway 官网。

自定义FilterFactory实现接口镜像

Gateway在处理http请求时,因为其遵循了Reactive Streams(响应式流规范),数据流只能被消费一次,即在通过 request.getBody() 获取的请求体,只能读取一次,无法对其进行直接编辑或复制。这有点类似于 Rust 语言风格,可以大幅提升内存效率。

那如果想多次处理请求体,则需要通过缓存请求体,并包装新的请求对象的方式来解决。

import com.mydemo.project.services.ApiLogger;
import com.mydemo.project.services.ServiceInvoker;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;/*** 镜像请求过滤器工厂类*/
@Slf4j
@Component
public class MirrorRequestFilterFactory extends AbstractGatewayFilterFactory<MirrorRequestFilterFactory.Config> {@Autowiredprivate ApiLogger apiLogger;@Autowiredprivate ServiceInvoker serviceInvoker;// 注入 Spring 自动配置的编解码器配置类,用于获取 HttpMessageReaderprivate final ServerCodecConfigurer codecConfigurer;// 构造器注入 ServerCodecConfigurerpublic MirrorRequestFilterFactory(ServerCodecConfigurer codecConfigurer) {super(Config.class);this.codecConfigurer = codecConfigurer;log.info("MirrorRequestFilter init");}@Overridepublic GatewayFilter apply(Config config) {log.info("request filtered, in apply");return (exchange, chain) -> {ServerHttpRequest request = exchange.getRequest();// 仅处理 POST 请求if (request.getMethod() != HttpMethod.POST) {return chain.filter(exchange);}// 读取并缓存原始请求体return DataBufferUtils.join(request.getBody()).flatMap(dataBuffer -> {// 复制请求体数据byte[] bytes = new byte[dataBuffer.readableByteCount()];dataBuffer.read(bytes);DataBufferUtils.release(dataBuffer); // 释放原始缓冲区// 创建新的DataBuffer用于原始请求继续使用Flux<DataBuffer> cachedFlux = Flux.defer(() -> {DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);DataBufferUtils.retain(buffer);return Mono.just(buffer);});// 创建装饰器请求,确保原始请求能继续使用请求体ServerHttpRequest decoratedRequest = new ServerHttpRequestDecorator(request) {@Overridepublic Flux<DataBuffer> getBody() {return cachedFlux;}};if (config.isMirrorEnabled()) {log.info("starting mirror request");// 使用复制的请求体发起新请求String copiedBody = new String(bytes, StandardCharsets.UTF_8);// 异步发起镜像请求(不阻塞主流程)serviceInvoker.invokeAsync(config.getMirrorUrl(), request, copiedBody);}// 将新的请求传入下一个 Filter 链,继续处理原始请求ServerWebExchange nextExchange = exchange.mutate().request(decoratedRequest).build();return chain.filter(nextExchange).doOnSuccess(response -> {apiLogger.log("primary", "success", request, response);}).doOnError(e -> {apiLogger.log("primary", "failure", request, e.getMessage());});});};}/*** 配置类*/@Datapublic static class Config {private String mirrorUrl; // 镜像 URLprivate boolean mirrorEnabled = true; // 是否启用镜像}
}

镜像功能的配置和启用

修改 application.yml,添加如下配置:

spring:cloud:gateway:routes:- id: route1uri: http://localhost:8088  # 模拟主API地址predicates:- Path=/api/reqfilters:- RewritePath=/api/req, /primary/req  # 网关代理主API地址- name: MirrorRequestFilterFactory  # 接口镜像功能过滤器args:mirrorUrl: http://127.0.0.1:8081/mirror/req  # 模拟镜像API地址mirrorEnabled: true  # 启用镜像功能

此处使用了两个过滤器。RewritePath过滤器,实现接收外部请求,并转换为对模拟主API地址的请求;
MirrorRequestFilterFactory,接口镜像功能过滤器,实现对模拟主API地址访问的同时,启用镜像API功能,实现对模拟镜像API地址的访问。

工程依赖版本

Spring Cloud Gateway 不同版本变化较大,当前官网最新版本为4.3.0,但考虑到要兼容目前最新版的Nacos等组件,工程依赖版本情况如下:

groupIdartifactIdversion
org.springframework.cloudspring-cloud-dependencies2023.0.3.3
org.springframework.bootspring-boot-dependencies3.3.13
org.springframework.cloudspring-cloud-starter-gateway4.1.9
org.springframework.cloudspring-cloud-starter-loadbalancer4.1.6
org.springframework.bootspring-boot-configuration-processor3.3.13
org.projectlomboklombok1.18.20

采用的Java版本:JDK17

最后,为实现接口陪跑,还需要实现异步发起镜像接口调用、主接口与镜像接口的交互日志记录、接口返回数据比对等功能,因与本次讨论主题无关,故略过。


文章转载自:

http://yHJB3rcr.qnxzx.cn
http://kDWMSdqr.qnxzx.cn
http://fDqgXeYG.qnxzx.cn
http://rD335ENU.qnxzx.cn
http://AaThaR4G.qnxzx.cn
http://asxtNRDa.qnxzx.cn
http://sqAcKZCa.qnxzx.cn
http://mUExIK3t.qnxzx.cn
http://oS6ApW6B.qnxzx.cn
http://rVStxxUi.qnxzx.cn
http://ubB9mnFP.qnxzx.cn
http://uaX0qT81.qnxzx.cn
http://IaplVIpE.qnxzx.cn
http://lFhRkYdb.qnxzx.cn
http://tXmJxEom.qnxzx.cn
http://Z2wNFCJu.qnxzx.cn
http://ElBT6ygg.qnxzx.cn
http://8nyh4uoq.qnxzx.cn
http://nCjWL1bl.qnxzx.cn
http://u2NcnHnT.qnxzx.cn
http://ZNnvQGDG.qnxzx.cn
http://4EyDOEqg.qnxzx.cn
http://pxvSNM0o.qnxzx.cn
http://KtKiJhvV.qnxzx.cn
http://Px9Ls2Is.qnxzx.cn
http://46qBLZOH.qnxzx.cn
http://waKJ9JuS.qnxzx.cn
http://i7HvgToz.qnxzx.cn
http://NmVXFaZu.qnxzx.cn
http://2fh2rLFc.qnxzx.cn
http://www.dtcms.com/a/364802.html

相关文章:

  • 数据库高可用全方案:Keepalived 故障切换 + LVS (DR) 模式 + MariaDB 主主同步实战案例
  • Web 集群高可用全方案:Keepalived+LVS (DR) 负载均衡 + Apache 服务 + NFS 共享存储搭建指南
  • TensorFlow的Yes/No 关键词识别模型训练
  • 从零开始的python学习——列表
  • VUE的模版渲染过程
  • 京东一面:假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?
  • Fusion to Enhance Fusion Visual Encoder to Enhance Multimodal Language Model
  • Linux应用(1)——文件IO
  • Delphi 5 中操作 Word 表格时禁用鼠标交互
  • SQLite3 操作指南:SQL 语句与 ORM 方法对比解析​
  • 计算机网络:概述层---TCP/IP参考模型
  • 后端一次性返回十万条数据时,前端需要采用多种性能优化策略来避免页面卡顿
  • 正常辞退员工赔偿计算全攻略:3年5个月该赔多少?
  • C++学习 part1
  • python中`__annotations__` 和 `inspect` 模块区别??
  • 【Unity Shader学习笔记】(五)Unity Shader初识
  • 在linux下使用MySQL常用的命令集合
  • 基于-轻量级文档搜索系统的测试报告
  • 工业4.0时代的通信革命:OPC UA Pub/Sub机制全面解析
  • 车载诊断架构 --- 从架构系统角度怎么确保整车DTC的完整性?
  • 关于缓存的一些思考?
  • SPI通讯协议
  • 【AI】人工智能 传统和现代 架构和算法的演变历史
  • 控制View缩放与还原
  • Go 语言面试题详解之上下文 (Context) 解密
  • 开学季 老师必备的收集信息“神器”
  • 利用 Python 绘制环形热力图
  • 使用飞算JavaAI快速搭建酒店管理系统
  • Augmentcode免费额度AI开发WordPress商城实战
  • VBA 64位API声明语句第013讲