Spring Cloud Gateway 常见过滤器的基本使用

目录

1. 过滤器的作用

2. Spring Cloud Gateway 过滤器的类型

2.1 内置过滤器

2.1.1 AddResponseHeader

2.1.2 AddRequestHeader

2.1.3 PrefixPath

2.1.4 RequestRateLimiter

2.1.5 Retry

2.2 自定义过滤器


1. 过滤器的作用

过滤器通常用于拦截、处理或修改数据流和事件流,在数据流中执行特定的操作或转换。

过滤器主要在以下几个方面发挥作用:

  1. 功能扩展和定制:过滤器允许您自定义和扩展网关的功能,以满足特定需求,如请求和响应的修改、路由规则的动态配置等。

  2. 数据校验和过滤:通过过滤器,您可以检查、验证和过滤传入或传出的数据,确保请求和响应的合法性和一致性。

  3. 安全保护:过滤器可以用于实施安全策略,如认证、授权、防止攻击等,以增强网关的安全性。

  4. 性能优化:通过过滤器,您可以对请求和响应进行性能优化,如缓存、压缩、请求路由的智能选择,以提高网关的性能。

  5. 统一处理:过滤器允许您在网关层面执行共享的处理逻辑,如日志记录、监控、审计等,以确保整个微服务体系的一致性和可维护性。

  6. 逻辑复用:通过过滤器,您可以将一些常见的操作抽象出来,以实现逻辑的复用,减少重复代码和维护工作。

2. Spring Cloud Gateway 过滤器的类型

Spring Cloud Gateway 过滤器可以分为两大类:

1. 内置过滤器

  • 局部的内置过滤器
  • 全局的内置过滤器

2. 自定义过滤器

2.1 内置过滤器

内置过滤器常见的有以下几种:

  1. AddResponseHeader
  2. AddRequestHeader
  3. AddRequestParameter(和 AddRequestHeader 相似)
  4. PrefixPath
  5. RequestRateLimiter
  6. Retry

Spring Cloud Gateway 过滤器常见有这么几种,实际上它有30多种,可以借助官方文档加以了解:Spring Cloud Gateway

过滤器又分为前置过滤器后置过滤器

在目标方法返回之前执行的过滤器就叫做前置过滤器(AddRequestXXX),在目标方法返回之后执行的过滤器就叫做后置过滤器。(AddResponseXXX)

前置工作:准备 user-service 和 order-service 两个模块,并且配置好 naocs 连接信息。

① user-service:创建一个 controller

@RestController
@RequestMapping("/user")
public class UserController {@Autowired  // 获取动态端口private ServletWebServerApplicationContext context;@RequestMapping("/getname")public String getName() {return context.getWebServer().getPort() +"--UserService:name=java-"+new Random().nextInt(100);}
}
server.port=0
spring.application.name=user-service-gateway
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos

② order-service:创建一个 controller

@RestController
@RequestMapping("/order")
public class OrderController {@RequestMapping("/getcount")public int getCount() {return new Random().nextInt(1000);}
}
server.port=0
spring.application.name=order-service-gateway
spring.cloud.nacos.discovery.server-addr=localhost:8848
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos

2.1.1 AddResponseHeader

spring:cloud:nacos: # 配置注册中心discovery:server-addr: localhost:8848username: nacospassword: nacosgateway: # 配置网关routes:- id: userserviceuri: lb://user-service-gateway   # loadbalancerpredicates:- Path=/user/**filters:- AddResponseHeader=My-Resp-Header, www.baidu
server:port: 10086

在配置过滤器之前,我在网关服务中引入 Nacos 和 LoadBalancer 之后,使用10086 端口去访问userservice服务时,查看对应的响应头:

配置了 AddResponseHeader 过滤器之后,再去访问userservice服务时,查看对应的响应头:

上述的设置,设置的是局部过滤器,如果此时在网关服务中再新增一组 id (如下),此时再去访问order-service服务时,响应头中就不会包含 My-Resp-Header 这个标头了。

spring:cloud:gateway: # 配置网关routes:- id: userservice  # 用户服务uri: lb://user-service-gateway   # loadbalancerpredicates:- Path=/user/**filters:- AddResponseHeader=My-Resp-Header, www.baidu- id: orderservice  # 订单服务uri: lb://order-service-gatewaypredicates:- Path=/order/**

此时还想访问 orderservice 服务时,也在响应头中看到对应的标头,要么就把 filters 照搬到下面,但是这样做,代码就不具备维护性了,发生修改的时候,这将会是一个体力活;要么就使用全局过滤器。

全局过滤器的配置

spring:cloud:gateway: # 配置网关routes:- id: userservice  # 用户服务uri: lb://user-service-gateway   # loadbalancerpredicates:- Path=/user/**filters:- AddResponseHeader=My-Resp-Header, www.baidu- id: orderservice  # 订单服务uri: lb://order-service-gatewaypredicates:- Path=/order/**default-filters:  # 全局过滤器- AddResponseHeader=MyApplication-Resp-Header, gateway.org

2.1.2 AddRequestHeader

spring:cloud:nacos: # 配置注册中心discovery:server-addr: localhost:8848username: nacospassword: nacosgateway: # 配置网关routes:- id: userservice  # 用户服务uri: lb://user-service-gateway   # loadbalancerpredicates:- Path=/user/**filters:- AddResponseHeader=My-Resp-Header, www.baidudefault-filters:- AddRequestHeader=My-Req-Marking, www.baidu

如何拿到前置过滤器,可以在 userservice 服务里边写一个打印请求头的 controller:

@RequestMapping("/print-header")
public void printHeader(HttpServletRequest request) {Enumeration<String> headers = request.getHeaderNames();while(headers.hasMoreElements()) {String key = headers.nextElement();String value = request.getHeader(key);System.out.println(key +": " + value);}
}

此时运行userservice 和 gateway,去反问 print-header 接口, 查看 userservice 的控制台:

2.1.3 PrefixPath

spring:cloud:nacos: # 配置注册中心discovery:server-addr: localhost:8848username: nacospassword: nacosgateway: # 配置网关routes:- id: userserviceuri: lb://user-service-gatewaypredicates:- Path=/user/**filters:- PrefixPath=/v2

假设此时 userservice 中的 UserController 升级为了 v2 版本,但是我的接口 /user/** 已经 对外公布了,此时还想让 localhost:10086/user/getname 访问到 v2 版本的 controller,肯定是不允许再修改后端的接口了,也不能让前端跟着改,使用 PrefixPath 就可以很好的调整。

第一版代码:

@RestController
@RequestMapping("/user")
public class UserController {@Autowired  // 获取动态端口private ServletWebServerApplicationContext context;@RequestMapping("/getname")public String getName() {return context.getWebServer().getPort() +"--UserService:name=java-"+new Random().nextInt(100);}
}

第二版代码: 

@RequestMapping("/v2/user")
@RestController
public class UserControllerV2 {@Autowired  // 获取动态端口private ServletWebServerApplicationContext context;@RequestMapping("/getname")public String getName() {return context.getWebServer().getPort() +"--V2:UserService:name=java-"+new Random().nextInt(10);}
}

当我们配置了 PrefixPath 之后,再使用原来的 localhost:10086/user/getname 去访问服务的时候,就可以正常的访问到 v2 版本的 controller 了,无需再去修改前后端接口。

2.1.4 RequestRateLimiter

这是 Spring Cloud Gateway 内置的网关限流过滤器,它使用了令牌桶的限流算法。

令牌桶限流算法:令牌桶限流算法通过固定速率生成令牌放入桶中,桶满则丢弃新令牌。请求到来时消耗令牌进行处理,桶内无令牌则等待或丢弃请求,从而平滑流量,防止网络拥堵。

Spring Cloud  Gateway 当前版本支持和 Redis 一起实现限流功能,Spring Cloud Gateway 选择 Redis 作为限流方案的一个重要支撑是因为 Redis 的一些特性可以很好地满足限流中对于性能、一致性和分布式处理的要求。

  1. 分布式环境:在微服务架构中,服务实例往往是分布式部署的,Redis 由于其天然的分布式特性,能够确保在不同的服务实例之间共享限流的状态,实现全局限流。

  2. 性能:Redis 是一个高性能的内存数据库,它的读写速度非常快,可以达到每秒数十万次的读写请求。这种性能上的优势使得 Redis 成为实现限流中维护和检查速率限制状态的理想选择。

  3. 原子操作:Redis 支持多种原子操作,这对于计数器来说非常重要。例如,使用 INCRDECR 命令递增或递减计数器,可以保证即使在高并发的情况下,计数器的值也是准确的。

  4. 过期策略:Redis 允许为数据设置生存时间(TTL),这对于限流算法中的时间窗口非常有用。例如,在固定时间窗口算法中,可以设置令牌或计数器在特定时间后自动过期。

它的实现步骤总共分为三步:

  1. 添加 Redis 框架依赖
  2. 创建限流规则
  3. 配置限流过滤器

a.添加 Redis 框架依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

注意事项:Redis版本太低,此功能设置会无效,建议使用 Redis 版本 5.x+。

b.创建限流规则

限流规则,既可以针对某一个 IP 做限流,也可以针对 URL 进行限流(所有的 IP 访问 URL 都会限流)

创建一个类,根据IP进行限流:

@Component
public class IpAddressKeyResolver implements KeyResolver {@Overridepublic Mono<String> resolve(ServerWebExchange exchange) {return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());}
}

创建一个类,根据URL进行限流:

@Component
public class UrlKeyResolver implements KeyResolver {@Overridepublic Mono<String> resolve(ServerWebExchange exchange) {// 获取请求的URI路径作为限流的keyString path = exchange.getRequest().getURI().getPath();return Mono.just(path);}
}

c.设置限流过滤器

spring:cloud:nacos: # 配置注册中心discovery:server-addr: localhost:8848username: nacospassword: nacosgateway: # 配置网关routes:- id: userserviceuri: lb://user-service-gatewaypredicates:- Path=/user/**filters:- name: RequestRateLimiterargs:redis-rate-limiter.replenishRate: 1  # 每秒请求数redis-rate-limiter.burstCapacity: 1  # 最大请求数key-resolver: "#{@ipAddressKeyResolver}"  # spEL表达式data:redis:host: 127.0.0.1port: 16379database: 0

注意,内置的限流过滤器 name 必须等于"RequestRateLimiter" ,其他参数的含义如下:

1. redis-rate-limiter.replenishRate:令牌填充速度(每秒允许请求数)

2. redis-rate-limiter.burstCapacity:令牌桶容量(最大令牌数)

3. key-resolver:根据哪个 key 进行限流,它的值是 spEL 表达式。

这三步完成之后,就表示每个 IP  每秒钟只能访问一次,如果刷新页面刷新的太快,就会出现如下页面:

2.1.5 Retry

在 OpenFeign 里面呢,也有个 Retry 超时重试,而且它还可以自定义重试规则,为什么 Gateway 还要有一个 Retry 呢 ?

OpenFeign里面的重试机制是服务调用层面的,它是用来帮助服务消费者处理调用远程服务时的问题的。而Spring Cloud Gateway的重试机制是在网关层面上,主要用于对所有通过网关的服务调用提供统一的重试策略,以处理上游服务可能出现的不稳定性(网络抖动)。两者虽都提供重试功能,但服务的层次不同,因此它们在微服务架构中各有其作用。

请求重试过滤器配置案例:

spring:cloud:nacos: # 配置注册中心discovery:server-addr: localhost:8848username: nacospassword: nacosgateway: # 配置网关routes:- id: userserviceuri: lb://user-service-gatewaypredicates:- Path=/user/**filters:- name: Retry  # 重试过滤器args:retries: 3statuses: GATEWAY_TIMEOUTmethods: GETseries: SERVER_ERRORbackoff:firstBackoff: 10ms  # 第一次重试间隔maxBackoff: 50ms  # 最大重试间隔factor: 2  # firstBack * (factor ^ n)   # 重试系数basedOnPreviousValue: false  # 基于上次重试时间加上重试系数来计算

注意,重试过滤器的 name 必须等于 "Retry" ,因为 "Retry" 就是内置重试过滤器的名字,改为其他框架就无法识别了;其他参数的含义如下:

1. retries:尝试的重试次数。

2. statuses:重试的HTTP状态码。取值参考:HttpStatus (Spring Framework 6.1.2 API)

3. methods:重试的HTTP方法。取值:GET(默认值),HEAD,POST,PUT,PATCH,DELETE,OPTIONS,TRACE。

4. series:要重试的一系列状态码。默认值是 SERVER_ERROR,值是 5,表示 5xx,( 5开头的状态码)。共有 5 个取值:

  • 1xx:INFORMATIONAL
  • 2xx:SUCCESSFUL
  • 3xx:REDIRECTION
  • 4xx:CLIENT_ERROR
  • 5xx:SERVER_ERROR

5. backoff:配置的重试策略。

  • firstBackoff:第一次的重试间隔
  • maxBackoff:最大重试间隔
  • factor:重试系数,firstBackoff *(factor ^ n),n= 1,2,3,4...
  • basedOnPreviousValue:默认关闭,如果设为 true,就表示基于上次的重试时间加上重试系数来计算,例如 firstBackoff 为 10,假设上一次重试是 10 * 2 ^ 1,那么下一次就不是 10 * 2 ^ 2,而是 20 * 2 ^ 2。

【案例演示】

在 userservice 服务中,写一个接口去触发 GATEWAY_TIMEOUT:

@RequestMapping("/504")
public void return504(HttpServletResponse response) {System.out.println("------- Do return504 method. ------");response.setStatus(504);
}

启动 userservice 模块和 gateway 模块,此时 userservice 服务自动注入到 nacos 中,使用原生服务接口访问:

原生服务接口:192.168.10.83:63129/user/504,触发 504 

此时查看控制台 :打印了一次

再使用 Gateway 网关去访问 userservice 服务, localhost:10086/user/504,触发 504

此时查看控制台:打印了 4 次(清除上面的打印后)

可见重试过滤器确实生效了。

【注意事项】

1. 此处的超时重试和 OpenFeign 里面的超时重试不太一样,这里设置重试 3 次,就是真的重试 3 次,加上触发重试的 1 次,总共就是打印 4 次,而 OpenFeign 里面的超时重试,重试次数的下标是从 1 开始的,所以在 OpenFeign 里面,这样设置,只会重试 2 次,打印 3 次。

2. 上面重试过滤器的配置,像 method、series 这种参数,它都是有默认值的,比如说 series 的默认值是 5 开头的状态码,那么即使我们不设置这个参数,当我们触发 5 开头的异常时,并且没有与其他设置的参数相悖的时候,也会触发超时重试功能。

2.2 自定义过滤器

代码案例:使用 Spring Cloud Gateway 提供的全局过滤器实现统一认证授权。

@Component
public class AuthFilter implements GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange,   // 执行的事件GatewayFilterChain chain)     // 过滤器链{// 未登录判断逻辑,当参数中 username=admin && password=admin 继续执行,// 否则退出执行// 得到 Request 对象 (reactive web)ServerHttpRequest request = exchange.getRequest();// 得到 Response 对象 (reactive web)ServerHttpResponse response = exchange.getResponse();String username = request.getQueryParams().getFirst("username");String password = request.getQueryParams().getFirst("password");if (username != null && username.equals("admin")&& password != null && password.equals("admin")) {// 已经登录,执行下一步return chain.filter(exchange);} else {// 设置无权限 401response.setStatusCode(HttpStatus.UNAUTHORIZED);// 执行完成,不用继续执行后续流程了return response.setComplete();}}
}

在访问相对应服务的时候,可以先不加用户名密码试一次,它会触发 401,再加上 username=admin&password=admin,就可以正常访问到相应服务了。

当有多个过滤器时,我们还可以给过滤器指定它的执行顺序:

假如说,此时有两个过滤器,一个用来验证登录,一个用来验证是否有权限。那么这个时候,需要先验证登录,才会再去验证是否有权限。如何实现?

  1. 实现 Ordered 接口
  2. 重写 getOrder 方法
@Override
public int getOrder() {// 此值越小越先执行return 1;
}

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

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

相关文章

PolarDB-X、OceanBase、CockroachDB、TiDB二级索引写入性能测评

为什么要做这个测试 二级索引是关系型数据库相较于NoSQL数据库的一个关键差异。二级索引必须是强一致的&#xff0c;因此索引的写入需要与主键的写入放在一个事务当中&#xff0c;事务的性能是二级索引性能的基础。 目前市面上的分布式数据库中&#xff0c;从使用体验的角度看…

【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作)

文章目录 &#x1f354;发放优惠券&#x1f386;基本操作&#x1f384;数据库表&#x1f6f8;思路&#x1f339;代码实现 &#x1f386;完善后的操作&#x1f6f8;乐观锁&#x1f339;代码实现 &#x1f354;一人仅一张优惠券&#x1f6f8;思路&#x1f339;代码⭐代码分析 &am…

关于“Python”的核心知识点整理大全44

目录 ​编辑 15.3.4 模拟多次随机漫步 rw_visual.py 注意 15.3.5 设置随机漫步图的样式 15.3.6 给点着色 rw_visual.py 15.3.7 重新绘制起点和终点 rw_visual.py 15.3.8 隐藏坐标轴 rw_visual.py 15.3.9 增加点数 rw_visual.py 15.3.10 调整尺寸以适合屏幕 rw_vi…

遥感影像辐射定标

遥感影像原始数据中每个像元代表地面光谱反射率的相对大小&#xff0c;叫做DN值。如果要 用于后续的反演或者生成一些反射率等产品时&#xff0c;必须要做辐射定标。 1.光学影像 对于普通光学影像来说&#xff0c;辐射定标可以输出两种&#xff1a;辐亮度和表观反射率…

【http】HTTP/1.0、HTTP/1.1和HTTP/2.0

✨ 专栏介绍 在当今互联网时代&#xff0c;计算机网络已经成为了人们生活和工作中不可或缺的一部分。而要实现计算机之间的通信和数据传输&#xff0c;就需要依靠各种网络协议来进行规范和约束。无论是浏览网页、发送电子邮件还是进行在线交流&#xff0c;都离不开各种各样的网…

C语言—每日选择题—Day64

前言 两天没更新了&#xff0c;作者在复习期末考试&#xff0c;更新一波&#xff0c;祝大家都能顺利通过期末考试&#xff01;&#xff01;&#xff01; 指针相关博客 打响指针的第一枪&#xff1a;指针家族-CSDN博客 深入理解&#xff1a;指针变量的解引用 与 加法运算-CSDN博…

Redis分布式缓存之主从哨兵分片集群

Redis主从 数据同步原理 Redis哨兵 Redis分片集群 集群伸缩&#xff1a;在集群中插入或删除某个节点 集群故障转移

Linux内核中断

Linux内核中断 ARM里当按下按键的时候&#xff0c;他首先会执行汇编文件start.s里面的异常向量表里面的irq,在irq里面进行一些操作。 再跳转到C的do_irq(); 进行操作&#xff1a;1&#xff09;判断中断的序号&#xff1b;2&#xff09;处理中断&#xff1b;3&#xff09;清除中…

tekton 发布 kubernetes 应用

tekton 发布 kubernetes 应用 基于Kubernetes 服务部署 Tekton Pipeline 实例&#xff0c;部署完成后使用tekton来完成源码拉取、应用打包、镜像推送和应用部署。 本文实现一个 golang-helloworld 项目 CI/CD 的完整流程&#xff0c;具体包括以下步骤&#xff1a; 从 gitee…

基于MATLAB的正态分布与卡方分布(附完整代码与例题)

目录 一. 理论部分 二. MATLAB所使用的函数介绍 2.1 概率密度函数 2.2 概率分布函数 2.3 逆概率分布函数 三. 例题与代码 例题1 例题2 例题3 例题4 一. 理论部分 将连续随机变量的概率密度函数记为&#xff0c;既然跟概率相关&#xff0c;那必然满足两个重要的性质&a…

加速布局!美格智能获国内某自主大厂智能座舱项目模组定点

近日&#xff0c;销售前线又传来重大好消息&#xff0c;美格智能座舱模组正式获得国内某自主大厂前装智能座舱项目定点。此次项目由主机厂直接定点模组&#xff0c;基于美格智能座舱模组SLM925来打造平台化智能座舱解决方案&#xff0c;同时此方案也将会应用于该汽车品牌及旗下…

图灵日记之java奇妙历险记--类和对象

目录 类的定义和使用类的定义格式 类的实例化类和对象的说明 this引用this引用的特性 对象的构造及初始化就地初始化构造方法 封装包导入包中的类自定义包 static成员static修饰成员变量static修饰成员方法 代码块代码块概念及分类构造代码块静态代码块 匿名对象 类的定义和使用…