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

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.xdnf.cn/news/3975.html

相关文章:

  • Flask + SQLite 简单案例
  • 位置权限关掉还能看到IP属地吗?全面解析定位与IP的关系
  • 腾讯云服务器技术全景解析:从基础架构到行业赋能​
  • React-router v7 第七章(导航)
  • 如何使用VSCode编写C、C++和Python程序
  • ES类迁移方法
  • 【翻译、转载】MCP 提示 (Prompts)
  • Kubernetes 安装 minikube
  • 计算机图形学编程(使用OpenGL和C++)(第2版) 01.环境搭建
  • Python的ArcPy基于Excel表格对大量遥感影像批量重分类
  • 第8章 Python 其他数据类型概述
  • LeetCode 1007. 行相等的最少多米诺旋转 题解
  • ZArchiver正版:高效文件管理,完美解压体验
  • 二、大模型原理:图文解析Transformer原理与代码
  • 第十章.XML
  • Android第三次面试总结之activity和线程池篇(补充)
  • C++基础算法:Dijkstra
  • Python 函数装饰器和闭包(变量作用域规则)
  • 基于k8s系统的API网关-kong网关
  • C++类与对象—下:夯实面向对象编程的阶梯
  • c++STL——set和map的使用
  • 5个情感丰富GPT-4o图像提示词(不是吉卜力风格)
  • transfomer网络构建
  • 平衡二叉搜索树模拟实现1-------AVL树(插入,删除,查找)
  • Fine Structure-Aware Sampling(AAAI 2024)论文笔记和启发
  • 交叉编译 opencv-4.10
  • [MATLAB]通过50个MATLAB程序理解信号与系统的核心概念
  • 学习黑客 TCP/IP
  • 【Springboot进阶】springboot+mybatis+jsqlparser实现数据权限控制
  • 57认知干货:AI机器人产业