Spring Cloud Gateway路由+断言+过滤

目录

    • 介绍
    • 核心功能
    • 三大核心
    • Route以服务名动态获取URL
    • Predicate常用断言
      • Path Route Predicate
      • After Route Predicate
      • Before Route Predicate
      • Between Route Predicate
      • Cookie Route Predicate
      • Header Route Predicate
      • Host Route Predicate
      • Query Route Predicate
      • RemoteAddr Route Predicate
      • Method Route Predicate
    • 自定义Predicate
    • 自定义Filter
      • 自定义全局Filter统计接口调用耗时情况
      • 自定义条件Filter
    • 总结

介绍


Spring Cloud Gateway 是 Spring Cloud 生态系统中的一个基于 Spring WebFlux 的 API 网关解决方案,旨在为微服务架构提供统一的入口点,支持路由、过滤、负载均衡等功能。它通过非阻塞的响应式编程模型,提供了高效、灵活的 API 网关能力。

核心功能


  • 动态路由: 支持基于路径、Header、Query参数等规则的动态路由。
  • 过滤器链: 通过全局过滤器 (Global Filter) 和局部过滤器 (Gateway Filter) 实现请求增强与响应修改。
  • 负载均衡: 集成Spring Cloud LoadBalancer,支持多实例流量分发。
  • 限流与熔断: 内置限流功能,支持 Redis 令牌桶算法,可结合 Resilience4j 实现熔断。
  • 安全与监控: 支持JWT、OAuth2等认证机制,保护后端服务。

三大核心


1. 路由 (Route)

路由是网关的核心,用于定义请求的转发规则。他由ID、目标URL、一系列的断言和过滤器组成,如果断言为true则路由匹配成功。一个路由包含以下属性:

  • ID: 路由的唯一标识。
  • URI: 目标服务的地址,支持 http、lb(负载均衡)等协议。
  • 断言: 定义路由匹配条件,如路径、Header、方法等。
  • 过滤器: 对请求或响应进行处理的逻辑。

2. 断言 (Predicate)

断言用于判断请求是否符合路由规则。常见的断言包括:

  • Path: 路径匹配。
  • Method: HTTP 方法匹配。
  • Header: 请求头匹配。
  • Query: 请求参数匹配。
  • After、Before、Between: 时间范围匹配。

3. 过滤 (Filter)

过滤器用于在请求转发前或响应返回后执行特定逻辑。常见的过滤器包括:

  • 全局过滤器: 对所有路由生效,如日志记录、认证校验。
  • 局部过滤器: 仅对特定路由生效,如路径重写、请求限流。

Route以服务名动态获取URL


网关服务 cloud-gateway9527 引入依赖

<!-- gateway -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 服务注册发现consul discovery,网关也要注册进服务注册中心统一管控 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-consul-discovery</artifactId><exclusions><exclusion><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId></exclusion></exclusions>
</dependency>
<!-- 指标监控健康检查的actuator -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- lombok -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>

yml配置

server:port: 9527spring:application:name: cloud-gateway# Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:service-name: ${spring.application.name}gateway:routes:- id: pay_routh1  # pay_routh1 (路由的ID,没有固定规则但要求唯一,建议配合服务名)uri: lb://cloud-payment-service  # 服务名predicates:- Path=/pay/gateway/get/**  # 断言,路径匹配的进行路由

订单服务 cloud-feign-order9002:OrderGatewayController

@RestController
public class OrderGatewayController {@Resourceprivate PayFeignApi payFeignApi;@GetMapping("/feign/pay/gateway/get/{id}")public Result getById(@PathVariable("id") Integer id) {return payFeignApi.getById(id);}
}

Feign接口 cloud-common-api:PayFeignApi

@FeignClient("cloud-gateway")
public interface PayFeignApi {@GetMapping("/pay/gateway/get/{id}")public Result getById(@PathVariable("id") Integer id);
}

支付服务 cloud-payment8001:PayGatewayController

@RestController
public class PayGatewayController {@Resourceprivate PayService payService;@GetMapping(value = "/pay/gateway/get/{id}")public Result<Pay> getById(@PathVariable("id") Integer id) {Pay pay = payService.getById(id);return Result.success(pay);}
}

测试结果

启动支付服务8001,启动订单服务9002,访问 http://localhost:9002/feign/pay/gateway/get/1 返回异常。再启动网关服务9527,访问 http://localhost:9002/feign/pay/gateway/get/1 返回成功。

Predicate常用断言


Path Route Predicate

spring:cloud:gateway:routes:- id: pay_routh1  # pay_routh1 (路由的ID,没有固定规则但要求唯一,建议配合服务名)uri: lb://cloud-payment-service  # 服务名predicates:- Path=/pay/gateway/get/**  # 断言,路径匹配的进行路由

测试结果

访问 http://localhost:9527/pay/gateway/get/1 返回成功。

After Route Predicate

# 获取当前时间串
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now);
spring:cloud:gateway:routes:- id: pay_routh1  # pay_routh1 (路由的ID,没有固定规则但要求唯一,建议配合服务名)uri: lb://cloud-payment-service  # 服务名predicates:- Path=/pay/gateway/get/**  # 断言,路径匹配的进行路由- After=2025-04-19T15:51:15.516781+08:00[Asia/Shanghai]  # 在某个时间之后可以访问

测试结果

2025-04-19 15:51:15 之前访问 http://localhost:9527/pay/gateway/get/1 返回失败。
2025-04-19 15:51:15 之后访问 http://localhost:9527/pay/gateway/get/1 返回成功。

Before Route Predicate

spring:cloud:gateway:routes:- id: pay_routh1  # pay_routh1 (路由的ID,没有固定规则但要求唯一,建议配合服务名)uri: lb://cloud-payment-service  # 服务名predicates:- Path=/pay/gateway/get/**  # 断言,路径匹配的进行路由- Before=2025-04-19T15:51:15.516781+08:00[Asia/Shanghai]  # 在某个时间之前可以访问

测试结果

2025-04-19 15:51:15 之前访问 http://localhost:9527/pay/gateway/get/1 返回成功。
2025-04-19 15:51:15 之后访问 http://localhost:9527/pay/gateway/get/1 返回失败。

Between Route Predicate

spring:cloud:gateway:routes:- id: pay_routh1  # pay_routh1 (路由的ID,没有固定规则但要求唯一,建议配合服务名)uri: lb://cloud-payment-service  # 服务名predicates:- Path=/pay/gateway/get/**  # 断言,路径匹配的进行路由- Between=2025-04-19T16:11:15.516781+08:00[Asia/Shanghai],2025-04-19T16:12:15.516781+08:00[Asia/Shanghai]  # 在某个时间段之间可以访问

测试结果

2025-04-19 16:11:15 到 2025-04-19 16:12:15 时间段之间访问 http://localhost:9527/pay/gateway/get/1 返回成功。

Cookie Route Predicate

spring:cloud:gateway:routes:- id: pay_routh1  # pay_routh1 (路由的ID,没有固定规则但要求唯一,建议配合服务名)uri: lb://cloud-payment-service  # 服务名predicates:- Path=/pay/gateway/get/**  # 断言,路径匹配的进行路由- Cookie=username,zzyy  # cookie中含有username=zzyy可以访问

测试结果

  • 原生命令测试
    curl http://localhost:9527/pay/gateway/get/1 --cookie “username=zzyy” 返回成功。
  • postman测试
    请求头中添加 cookie:username=zzyy,访问 http://localhost:9527/pay/gateway/get/1 返回成功。

Header Route Predicate

spring:cloud:gateway:routes:- id: pay_routh1  # pay_routh1 (路由的ID,没有固定规则但要求唯一,建议配合服务名)uri: lb://cloud-payment-service  # 服务名predicates:- Path=/pay/gateway/get/**  # 断言,路径匹配的进行路由- Header=X-Request-Id,\d+  # 请求头中含有X-Request-Id且值为整数的正则表达式可以访问

测试结果

  • 原生命令测试
    curl http://localhost:9527/pay/gateway/get/1 -H “X-Request-Id:123” 返回成功。
  • postman测试
    请求头中添加 X-Request-Id:123,访问 http://localhost:9527/pay/gateway/get/1 返回成功。

Host Route Predicate

spring:cloud:gateway:routes:- id: pay_routh1  # pay_routh1 (路由的ID,没有固定规则但要求唯一,建议配合服务名)uri: lb://cloud-payment-service  # 服务名predicates:- Path=/pay/gateway/get/**  # 断言,路径匹配的进行路由- Host=**.zzyy.com  # 主机地址后必须含有.zzyy.com可以访问

测试结果

  • 原生命令测试
    curl http://localhost:9527/pay/gateway/get/1 -H “Host:www.zzyy.com” 返回成功。
  • postman测试
    请求头中添加 Host:www.zzyy.com,访问 http://localhost:9527/pay/gateway/get/1 返回成功。

Query Route Predicate

spring:cloud:gateway:routes:- id: pay_routh1  # pay_routh1 (路由的ID,没有固定规则但要求唯一,建议配合服务名)uri: lb://cloud-payment-service  # 服务名predicates:- Path=/pay/gateway/get/**  # 断言,路径匹配的进行路由- Query=uid,\d+  # 请求参数必须含有uid且值为整数的正则表达式可以访问

测试结果

访问 http://localhost:9527/pay/gateway/get/1?uid=123 返回成功。

RemoteAddr Route Predicate

spring:cloud:gateway:routes:- id: pay_routh1  # pay_routh1 (路由的ID,没有固定规则但要求唯一,建议配合服务名)uri: lb://cloud-payment-service  # 服务名predicates:- Path=/pay/gateway/get/**  # 断言,路径匹配的进行路由- RemoteAddr=192.168.42.1/24  # 远程访问地址必须是192.168.42.xx才能访问

测试结果

当前电脑IP为 192.168.42.3,访问 http://192.168.42.3:9527/pay/gateway/get/1 返回成功。

Method Route Predicate

spring:cloud:gateway:routes:- id: pay_routh1  # pay_routh1 (路由的ID,没有固定规则但要求唯一,建议配合服务名)uri: lb://cloud-payment-service  # 服务名predicates:- Path=/pay/gateway/get/**  # 断言,路径匹配的进行路由- Method=GET,POST  # get/post请求可以访问

测试结果

get 请求访问 http://localhost:9527/pay/gateway/get/1 返回成功。

自定义Predicate


网关服务 cloud-gateway9527 中新建 MyRoutePredicateFactory

// 自定义配置会员等级,按照 铂、金、银和yml配置的会员等级,才可以访问
// 继承 AbstractRoutePredicateFactory
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {// 无参构造方法public MyRoutePredicateFactory() {super(MyRoutePredicateFactory.Config.class);}// 短格式public List<String> shortcutFieldOrder() {return Collections.singletonList("userType");}// 重写apply方法@Overridepublic Predicate<ServerWebExchange> apply(Config config) {return new Predicate<ServerWebExchange>() {@Overridepublic boolean test(ServerWebExchange serverWebExchange) {// 检查request参数中是否存在userType,且值和config中的相同,则可以访问String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");return userType != null && userType.equals(config.getUserType());}};}// 这个Config类就是路由断言规则public static class Config {private @NotNull String userType;  // 铂、金、银和yml配置的会员等级public Config() {}public String getUserType() {return userType;}public void setUserType(String userType) {this.userType = userType;}}
}

yml配置

server:port: 9527spring:application:name: cloud-gateway# Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:service-name: ${spring.application.name}gateway:routes:- id: pay_routh1  # pay_routh1 (路由的ID,没有固定规则但要求唯一,建议配合服务名)uri: lb://cloud-payment-service  # 服务名predicates:- Path=/pay/gateway/get/**  # 断言,路径匹配的进行路由- My=gold  # 自定义断言

测试结果

访问 http://localhost:9527/pay/gateway/get/1?userType=gold 返回成功。

自定义Filter


自定义全局Filter统计接口调用耗时情况

网关服务 cloud-gateway9527 中新建 MyGlobalFilter

@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {private static final String START_TIME = "start_time";  // 开始调用方法的时间@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 记录开始时间exchange.getAttributes().put(START_TIME, System.currentTimeMillis());return chain.filter(exchange).then(Mono.fromRunnable(() -> {Long startTime = exchange.getAttribute(START_TIME);if (startTime != null) {URI uri = exchange.getRequest().getURI();log.info("访问接口主机:" + uri.getHost() + ",端口:" + uri.getPort() +",URL:" + uri.getPath() + ",参数:" + uri.getRawQuery() +",时长:" + (System.currentTimeMillis() - startTime) + "毫秒");}}));}// 值越小,优先级越高@Overridepublic int getOrder() {return 0;}
}

测试结果

访问 http://localhost:9527/pay/gateway/get/1 日志输出:访问接口主机:localhost,端口:9527,URL:/pay/gateway/get/1,参数:null,时长:6毫秒

自定义条件Filter

// 继承 AbstractGatewayFilterFactory
@Component
@Slf4j
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config>  {// 无参构造方法public MyGatewayFilterFactory() {super(MyGatewayFilterFactory.Config.class);}// 短格式public List<String> shortcutFieldOrder() {return Arrays.asList("state");}// 重写apply方法@Overridepublic GatewayFilter apply(MyGatewayFilterFactory.Config config) {return new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();log.info("进入自定义条件过滤器MyGatewayFilterFactory, state=" + config.getState());if (request.getQueryParams().containsKey("zzyy")) {return chain.filter(exchange);}exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);return exchange.getResponse().setComplete();}};}public static class Config {private String state;public Config() {}public String getState() {return state;}public void setState(String state) {this.state = state;}}
}

yml配置

server:port: 9527spring:application:name: cloud-gateway# Spring Cloud Consul for Service Discoverycloud:consul:host: localhostport: 8500discovery:service-name: ${spring.application.name}gateway:routes:- id: pay_routh1  # pay_routh1 (路由的ID,没有固定规则但要求唯一,建议配合服务名)uri: lb://cloud-payment-service  # 服务名predicates:- Path=/pay/gateway/get/**  # 断言,路径匹配的进行路由filters:- My=zzyy  # 自定义条件过滤器

测试结果

访问 http://localhost:9527/pay/gateway/get/1?zzyy=16 返回成功。

总结


以上主要介绍了 Spring Cloud Gateway 路由、断言、过滤的相关知识,以及自定义 Predicate 和 Filter,想了解更多 Spring Cloud Gateway 知识的小伙伴请参考 Spring Cloud Gateway 官网 进行学习,学习更多 Spring Cloud 实战实用技巧的小伙伴,请关注后期发布的文章,认真看完一定能让你有所收获。

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

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

相关文章

springboot集成langchain4j记忆对话

流式输出 LLM 一次生成一个标记&#xff08;token&#xff09;&#xff0c;因此许多 LLM 提供商提供了一种方式&#xff0c;可以逐个标记地流式传输响应&#xff0c;而不是等待整个文本生成完毕。 这显著改善了用户体验&#xff0c;因为用户不需要等待未知的时间&#xff0c;几…

【SpringCloud GateWay】Connection prematurely closed BEFORE response 报错分析与解决方案

一、背景 今天业务方调用我们的网关服务报错: Connection prematurely closed BEFORE response二、原因分析 三、解决方案 第一步: 增加 SCG 服务的JVM启动参数,调整连接获取策略。 将连接池获取策略由默认的 FIFO&#xff08;先进先出&#xff09;变更为 LIFO&#xff08…

使用ZYNQ芯片和LVGL框架实现用户高刷新UI设计系列教程(第十一讲)

这一期讲解lvgl中下拉框的基础使用&#xff0c;下拉列表允许用户从选项列表中选择一个值&#xff0c;下拉列表的选项表默认是关闭的&#xff0c;其中的选项可以是单个值或预定义文本。 当单击下拉列表后&#xff0c;其将创建一个列表&#xff0c;用户可以从中选择一个选项。 当…

【神经网络与深度学习】VAE 在解码前进行重参数化

在 VAE 中&#xff0c;解码之前进行重参数化主要有以下几个重要原因&#xff1a; 可微分性 在深度学习里&#xff0c;模型是通过反向传播算法来学习的&#xff0c;而这需要计算梯度。若直接从潜在变量的分布 (q_{\theta}(z|x))&#xff08;由编码器输出的均值 (\mu) 和方差 (…

BBDM学习笔记

1. configs 1.1 LBBDM: Latent BBDM [readme]

mysql主从复制搭建,并基于‌Keepalived + VIP实现高可用

以下是基于 ‌Keepalived VIP‌ 实现 MySQL 主从复制高可用的详细步骤&#xff0c;涵盖主从复制搭建与故障自动切换&#xff1a; 一、MySQL 主从复制搭建&#xff08;基础步骤回顾&#xff09; 1. ‌主库&#xff08;Master&#xff09;配置‌ 修改配置文件‌ /etc/my.cnf&…

CD36.【C++ Dev】STL库的string的使用 (下)

目录 1.reserve函数(不是reverse) 代码示例 2.resize 代码示例 3.reserve和resize的区别 4.shrink_to_fit 代码示例 5.与C语言的配合的接口函数: c_str 代码示例 6.rfind 知识回顾:find函数 rfind 代码示例 练习题: 字符串最后一个单词的长度 代码 提交结果 ​…

STM32的网络天气时钟项目

一、项目概述与硬件架构 1.1 核心功能 本智能天气时钟系统集成了实时天气获取、网络时间同步、环境监测和低功耗管理四大核心功能&#xff1a; 网络数据获取&#xff1a; 通过ESP8266 WiFi模块连接心知天气API&#xff08;每小时更新&#xff09;获取北京标准时间服务器的时…

FPGA DDR4多通道管理控制器设计

DDR4控制器一般采用自带的MIG控制器&#xff0c;用户控制主要是基于MIG IP核进行设计 实际工程项目中可能只挂载了一组DDR&#xff0c;但是用户数据可能有很多种&#xff0c;用户通过给每种数据划分特定地址进行存储&#xff0c;如何实现灵活管理成为设计的关键 为了方便后端数…

低代码 x AI,解锁数智化应用的创新引擎

AI 智能体开发指南 随着全球信息化浪潮的持续推进&#xff0c;数字化、智能化转型已成为企业发展的必经之路。在这个变革的时代&#xff0c;企业面临着前所未有的挑战与机遇。一方面&#xff0c;市场环境瞬息万变&#xff0c;企业需要快速响应并调整业务模式&#xff1b;另一方…

【Spring Boot 注解】@Configuration与@AutoConfiguration

文章目录 Configuration与AutoConfiguration一、Configuration二、AutoConfiguration Configuration与AutoConfiguration 一、Configuration 这是最常用的 Spring 注解之一&#xff0c;表示当前类是一个 配置类&#xff0c;可以定义 Bean 方法&#xff0c;等效于传统的 XML 配…

arXiv论文 MALOnt: An Ontology for Malware Threat Intelligence

文章讲恶意软件威胁情报本体。 作者信息 作者是老美的&#xff0c;单位是伦斯勒理工学院&#xff0c;文章是2020年的预印本&#xff0c;不知道后来发表在哪里&#xff08;没搜到&#xff0c;或许作者懒得投稿&#xff0c;也可能是改了标题&#xff09;。 中心思想 介绍开源…

【存储管理—动态不等长存储资源分配算法】

文章目录 一、实验目的二、实验内容与设计思想实验内容设计思路 三、实验代码实现四、总结 一、实验目的 理解动态异长存储分区资源管理&#xff0c;掌握所需数据结构和管理程序&#xff0c;了解各种存储分配算法的优点和缺点。 二、实验内容与设计思想 实验内容 1.分析uni…

快速上手 Docker:从入门到安装的简易指南(Mac、Windows、Ubuntu)

PS&#xff1a;笔者在五一刚回来一直搞Docker部署AI项目&#xff0c;发现从开发环境迁移到生成环境时&#xff0c;Docker非常好用。但真的有一定上手难度&#xff0c;推荐读者多自己尝试踩踩坑。 本篇幅有限&#xff0c;使用与修改另起篇幅。 一、Docker是什么 #1. Docker是什…

LabVIEW高冲击加速度校准系统

在国防科技领域&#xff0c;高 g 值加速度传感器广泛应用于先进兵器研制&#xff0c;如深侵彻系统、精确打击弹药及钻地弹药等。其性能指标直接影响研究结果的准确性与可靠性&#xff0c;因此对该传感器进行定期校准意义重大。高冲击加速度校准系统具备多方面功能&#xff0c;适…

FPGA 纯逻辑NVME raid0 IP核

系统采用XCZU19EG搭载4个三星990 PRO SSD 单盘读写不低于3GB/s 4盘总带宽不低于12GB/s

GStreamer开发笔记(三):测试gstreamer/v4l2+sdl2/v4l2+QtOpengl打摄像头延迟和内存

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://blog.csdn.net/qq21497936/article/details/147714800 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、O…

CATIA高效工作指南——零件建模篇(二)

一、PowerCopy特征复用技术 1.1 智能特征封装 通过​​几何图形集(Geometrical Set)​​构建参数化特征组&#xff0c;将关联的草图、曲面、实体等元素进行逻辑封装。操作流程如下&#xff1a; 创建新几何图形集并完成特征建模激活PowerCopy命令&#xff0c;选择目标几何集定…

CentOS 7 安装OpenJDK 17 JRE

CentOS 7 自带的java 版本为&#xff1a;java version "1.8.0_311"&#xff0c; 有些软件的运行需要更高的java版本。CentOS 7 自带的默认仓库里 没有 OpenJDK 17&#xff0c;但是 Adoptium 项目&#xff08;前身 AdoptOpenJDK&#xff09;提供了稳定的 OpenJDK 17 版…

【c++】 我的世界

太久没更新小游戏了 给个赞和收藏吧&#xff0c;求求了 要游戏的请私聊我 #include <iostream> #include <vector>// 定义世界大小 const int WORLD_WIDTH 20; const int WORLD_HEIGHT 10;// 定义方块类型 enum BlockType {AIR,GRASS,DIRT,STONE };// 定义世界…