Spring Cloud Gateway 实现简单自定义过滤器

news/2024/9/12 15:07:11/文章来源:https://www.cnblogs.com/ghimi/p/18355976

背景

Spring Cloud Gateway 是 Spring Cloud 退出的第二代网关框架,我们可以用它来实现 反向代理,路由转发,权限校验等功能,这里介绍一个它的基础功能,通过 Filter 机制实现一个简单的 HTTP 接口处理。

从总体上来看 Spring Cloud Gateway 提供的过滤器可以分为两类,一种是对全局流量都生效的全局过滤器(Global Filter),另外一种是针对特定路径生效的自定义过滤器;通过全局过滤器我们可以实现一些全局请求的处理操作,如请求性能监控,请求日志记录,请求缓存等;通过自定义过滤器我们可以针对特定的请求实现一些特定的请求处理,如添加请求头,添加参数,添加响应头等功能等。

Spring Cloud Gateway 的过滤器处理是经典的 23 种设计模式中的责任链模式(想要学习设计模式的童鞋可以去翻源码)。当一个请求满足路由规则时,负责过滤网络请求的处理器会将全部 GlobalFilter 和特定路由实现的 Filter 添加到过滤器链中,多个 Filter 的排序通过 Ordered 接口实现,用户可以通过手动实现 Ordered 接口中的 getOrder 方法来指定实际执行的顺序, getOrder 返回的数字越小,表示执行的优先级越高,即 Filter 会被越早调用。

img

当一个请求进入到应用中后,会在请求实际处理前(pre),和结果处理后(post)经过每个 Filter 各一次,我们可以通过 Filter 实现对请求的记录,拦截,日志打印,执行时间记录等功能。

Spring Cloud Gateway 默认已经提供了一些 Filter 供开发者使用,GatewayMetricsFilter 可以用来记录一些指标,通过 spring-boot-starter-actuator 库可以讲这些指标透传出去,可以实现请求内容和响应结果的分析。也可以实现一个告警系统,在系统出现异常请求时及时发起告警。LocalResponseCache 则可以将请求的响应结果缓存下来,这样对于一些重复的请求就可以减少对代理系统的负载,从而提升整体系统的性能。路由转发 Filter ForwardRoutingFilter则可以实现请求的转发,通过修改原始请求路径后,将新的请求转发到目标节点上。NettyRoutingFilter 可以通过 Netty 的 HttpClient 生成下游的请求,然后将响应转发到后续的 Filter 中。除此之外还有一些其他的过滤器,可以通过这些过滤器灵活组合实现想要的功能。

除了 GlobalFilter 之外,还有一类 Filter 的职责没有那么广,他们是专门针对特定的请求实现一些特定的功能,这些 Filter 通过配置的规则作用到目标的请求上,完成一些增强功能。如 AddRequestHeader 可以为进入的请求添加额外的 header ,可以供后面的代理服务器鉴权认证使用;AddRequestParameter 可以添加额外的请求参数,AddResponseHeader 用于添加额外的响应头...诸如此类。

首先用 Spring Starter 创建一个 Spring Cloud Gateway 应用,然后添加配置,为指定 url 增加一个过滤器:

spring:cloud:gateway: #网关路由配置routes:- id: user-grpc #路由 id,没有固定规则,但唯一,建议与服务名对应uri: https://[::1]:443 #匹配后提供服务的路由地址predicates:#以下是断言条件,必选全部符合条件- Path=/**               #断言,路径匹配 注意:Path 中 P 为大写- Header=Content-Type,application/json # 断言,请求头匹配:filters:- CustomFilter #自定义过滤器名称
@Slf4j
@Component
public class CustomFilterFactory extends AbstractGatewayFilterFactory<Object> {public CustomFilterFactory(ResourceLoader resourceLoader) {super(Object.class);}@Overridepublic GatewayFilter apply(Object config) {GatewayFilter filter = new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {CustomResponseDecorator modifiedResponse = new CustomResponseDecorator(exchange);ServerWebExchange newExchange = exchange.mutate().response(modifiedResponse).build();// 跳过后续自动路由到下游代理ServerWebExchangeUtils.setAlreadyRouted(newExchange);return modifiedResponse.writeWith(exchange.getRequest().getBody()).then(chain.filter(newExchange));}@Overridepublic String toString() {return filterToStringCreator(CustomFilterFactory.this).toString();}};// 设置 Filter 优先级在写入响应前int order = NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;return new OrderedGatewayFilter(filter, order);}@Overridepublic String name() {return "CustomFilter";}@Slf4jstatic class CustomResponseDecorator extends ServerHttpResponseDecorator {/*** 返回成功的消息*/public static final HttpMsg SUCCESS_MESSAGE;private final ServerWebExchange exchange;static {SUCCESS_MESSAGE = new HttpMsg();SUCCESS_MESSAGE.setCode(200);SUCCESS_MESSAGE.setMessage("success");}public CustomResponseDecorator(ServerWebExchange exchange) {super(exchange.getResponse());this.exchange = exchange;}@Overridepublic Mono<Void> writeWith(Publisher<? extends DataBuffer> bodyPub) {ServerHttpRequest request = exchange.getRequest();HttpMethod method = request.getMethod();String path = request.getPath().value();long contentLength = request.getHeaders().getContentLength();String clientIp = Optional.ofNullable(request.getRemoteAddress()).map(InetSocketAddress::getAddress).map(InetAddress::toString).orElse(null);Mono<String> reqBodyMono;// 当请求包含 body 时,解析 body 内容为 stringif (contentLength > 0) {reqBodyMono = DataBufferUtils.join(bodyPub).mapNotNull(buffer -> {String str = buffer.toString(StandardCharsets.UTF_8);// 解析完成后,释放释放 bufferDataBufferUtils.release(buffer);return str;});} else {// 不包含 body 时,直接返回空字符串reqBodyMono = Mono.just("");}Mono<DataBuffer> res = reqBodyMono.handle((body, sink) -> {// 记录请求信息log.debug("receive request client ip:{} path: {}({}),body: {}", clientIp, path, method, body);sink.next(SUCCESS_MESSAGE);}).map(resMsg -> {getHeaders().setContentType(MediaType.APPLICATION_JSON);return JSONUtil.toJsonStr(resMsg);}).map(str -> bufferFactory().wrap(ByteBuffer.wrap(str.getBytes(StandardCharsets.UTF_8))));return super.writeWith(res);}}
}

参考资料

  • Global Filters
  • Configuring Route Predicate Factories and Gateway Filter Factories

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java手写RPC框架系列-1】—— 基础知识准备:RPC+Netty

代码随想录知识星球介绍 https://articles.zsxq.com/id_m76jd72243bi.html 基于Netty手写实现RPC https://www.cnblogs.com/mic112/p/15565795.html项目背景与介绍RPC:远程过程调用协议:客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用…

非线性规划的经典例题--选址问题

本章会介绍如何利用非线性规划解决选址问题,这个问题是文章线性规划在数学建模中的两道例题中第二道投料问题的第二小题,本章为基于这道题的基础上进行介绍,建议读者返回去看一看 目录一、问题提出二、问题分析三、模型建立四、代码实现1.输入目标函数2.输入线性约束 一、问…

了解10X文库组成

转的文章 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/LittleComputerRobot/article/details/137207083R1: 26 表示10X barcode 的 16bp碱基 + 10bp UMI; i7: 8表示 8bp 样本index序列 Read…

Java数组07:稀疏数组

1. 线性结构线性结构是最常用的数据结构,其特点是数据元素之间存在一对一的线性关系。 线性结构有两种不同存储结构,即顺序存储结构和链式存储结构。 顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的,即在内存中是连续的,例如数组。 链式存储的线性表称为链表,链表…

【Django开发】django美多商城项目完整开发4.0第2篇:项目准备【附代码文档】

本教程的知识点为:美多商城 项目准备 项目准备 配置 1. 修改settings/dev.py 文件中的路径信息 2. INSTALLED_APPS 3. 数据库 用户部分 图片验证码 1. 后端接口设计: 视图原型 2. 具体视图实现 用户部分 使用Celery完成发送短信 判断帐号是否存在 1. 判断用户名是否存在 后端…

IntelliJ IDEA 单元测试插件 TestMe

IntelliJ IDEA 添加单元测试插件 TestMe

Redis 使用场景

Redis 使用场景前三种使用广泛,另加队列用

Datawhale X 魔搭 AI夏令营 AIGC方向 Task2

代码逐行解析 先记录一下baseline代码通义千问生成的逐行解析: # 安装 Data-Juicer 和 DiffSynth-Studio !pip install simple-aesthetics-predictor # 安装simple-aesthetics-predictor !pip install -v -e data-juicer # 安装data-juicer !pip uninstall pytorch-lightning …

[Paper Reading] Multiple View Geometry Transformers for 3D Human Pose Estimation

Multiple View Geometry Transformers for 3D Human Pose Estimation link 时间:CVPR2024 机构:University of Toronto && Southeast University && Microsoft Research Asia TL;DR 提出一种基于Transformer端到端3D Human Pose Estimation方法MVGFormer,核…

MySQL8.0 Clone Plugin 实现解析浅析

MySQL8.0 Clone Plugin 实现解析浅析 从8.0.17版本开始官方实现了clone的功能,允许用户通过简单的SQL命令把远端/本地的数据库实例拷贝到其他实例后快速拉起一个新的实例。 该功能由一些列的WL组成 :Clone local replica(WL#9209) : 实现了数据本地Clone。 Clone remote repli…

USB协议详解第8讲(USB描述符-字符串和语言ID描述符)

1.字符串描述符相关概念 字符串描述符:首先,字符串描述符就是用字符串描述一个设备的一些属性,毕竟人能看懂的是字符,而不是十六进制,描述的属性包括设备厂商名字、产品名字、产品序列号、各个配置名字、各个接口名字,还有就是由我们用户自己定义的字符串,说白了就是起名…

【Azure Developer】使用Python SDK去Azure Container Instance服务的Execute命令的疑问解释

Azure 容器实例(Azure Container Instances,简称 ACI)是一个无服务器容器解决方案,允许用户在 Azure 云环境中运行 Docker 容器,而无需设置虚拟机、集群或编排器。ACI 适用于任何可以在隔离容器中操作的场景,包括事件驱动的应用程序、从容器开发管道快速部署、数据处理和…