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

SpringCloud系列 - Gateway 网关功能(五)

目录

一、介绍

二、创建项目

三、初体验 - 基本路由

3.1 规则配置

 3.2 工作原理

(1)​​核心组件​​

(2)​​请求处理流程​​

(3)其他配置项:order

四、断言

4.1 路径(Path)断言

4.2 请求参数(Query)断言

4.3 自定义断言工厂🔥

(1)创建断言工厂类

(2)定义配置类

(3)重写apply方法

(4)重写shortcutFieldOrder方法(可选)

4.4 自定义断言工厂示例一

4.5 自定义断言工厂示例二

4.6 自定义断言工厂的注意事项

五、过滤器

5.1 路径重写(RewritePath)过滤🔥

5.2 添加响应头(AddResponseHeader) 过滤

5.3 内置过滤器清单

5.4 default-filters

5.5 Global Filter

(1)实现接口

(2)基本实现步骤

(3)示例代码

5.6 自定义过滤器工厂

(1)继承抽象基类

(2)定义配置类

(3)重写apply方法

(4)重写shortcutFieldOrder方法(可选)

(5)配置 

5.7 自定义过滤器工厂示例

六、扩展 - 跨域处理

七、微服务之间的远程调用经过网关吗?


一、介绍

Gateway(网关)是微服务架构中的​​统一入口​​,负责接收所有客户端请求并转发至后端服务,同时集成路由、安全、监控等非业务功能。

其核心作用包括:

  1. ​路由转发​
    根据请求路径(如/user/**)、Header、参数等动态匹配目标服务,例如将/api/user转发至用户服务user-service
  2. ​负载均衡​
    集成服务发现(如Nacos、Eureka),通过轮询、随机等策略分发请求至多个服务实例。
  3. ​安全控制​
    统一处理认证(JWT、OAuth2)、鉴权、IP白名单等,避免重复实现。
  4. ​流量治理​
    支持限流(令牌桶算法)、熔断(如集成Sentinel)、降级等,保护后端服务。
  5. ​协议转换​
    处理HTTP/gRPC/WebSocket等协议转换,适配异构系统。

🔥学习网关的核心:掌握网关相关的配置!!!

二、创建项目

我们单独创建一个模块,就叫gateway,简单明了。

pom文件 

    <dependencies><!-- 网关 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!-- 注册中心 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!-- 配置中心 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><!-- 负载均衡 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency></dependencies>

配置文件 

server:port: 80
spring:application:name: gatewayprofiles:active: devcloud:nacos:server-addr: 127.0.0.1:8848discovery:namespace: ${spring.profiles.active:public}config:namespace: ${spring.profiles.active:public}---
spring:config:import:- nacos:common.yaml?group=gatewayactivate:on-profile: dev---
spring:config:import:- nacos:common.yaml?group=gatewayactivate:on-profile: test---
spring:config:import:- nacos:common.yaml?group=gatewayactivate:on-profile: prod

创建主程序

@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}
}

启动项目 

 

三、初体验 - 基本路由

3.1 规则配置

值得注意的是

使用断言为各服务配置不同请求前缀后,别忘了在各个服务的请求前面一定要加上该请求前缀。具体的可以通过请求注解加上,也可以通过配置文件统一上下文配置好。

另外不要忘记远程调用客户端中的请求路径也要加上对应的前缀。

 3.2 工作原理

(1)​​核心组件​

​Route(路由)​​:定义ID、目标URI、断言(Predicate)和过滤器(Filter)。

​Predicate(断言)​​:匹配请求条件(如Path=/api/**),决定是否路由。

​Filter(过滤器)​​:在请求前后执行逻辑(如修改Header、限流)。

spring:cloud:gateway:routes:- id: edu-route           # 路由的id,没有固定规则但要求唯一,建议与服务名对应uri: lb://service-edu   # lb 是负载均衡转发的意思predicates:             # 断言,路径相匹配的进行路由- Path=/edu/**        # 将/edu/**路径转发到service-edu- id: system-routeuri: lb://service-systempredicates:- Path=/system/**

(2)​​请求处理流程​

  1. 客户端请求到达Gateway,由 DispatcherHandler 接收。
  2. RoutePredicateHandlerMapping 根据断言匹配路由。
  3. FilteringWebHandler 执行过滤器链(Pre→代理请求→Post)。
  4. 通过Netty Client转发请求至目标服务,返回响应。

(3)其他配置项:order

顺序优先级,不写默认排在前面的优先。

这在路由规则冲突的情况下,使用起来很明显。

举个例子:

如果我们把这个百度的/**路径放在最前面,那么再访问http://localhost/edu/test/getRemoteFeign路径就找不到了。

于是,使用上order的作用就出来了。不管谁写在前面谁写在后面,顺序都不重要了,优先级是按照order的数值决定,数值越小优先级越高。

四、断言

Route Predicate Factories :: Spring Cloud Gateway

参数(个数/类型)作用
After1/datetime在指定时间之后
Before1/datetime在指定时间之前
Between2/datetime在指定时间区间内
Cookie2/string,regexp包含cookie名且必须匹配指定值
Header2/string,regexp包含请求头且必须匹配指定值
HostN/string请求host必须指定枚举值
MethodN/string请求方式必须指定枚举值
Path2/List<String>, bool请求路径满足规则,是否匹配最后的 /
Query2/string, regexp包含指定请求参数
RemoteAddr1/List<String>请求来源于指定网络域(CIDR写法)
Weight2/string, int按指定权重负载均衡
XForwardedRemoteAddr1/List<String>从X-Forwarded-For请求头中解析请求来源,并判断是否来源于指定网络域

4.1 路径(Path)断言

短写法

predicates:- Path=/edu/**

全写法

predicates:- name: Pathargs:pattern: /edu/**

 4.2 请求参数(Query)断言

源码中显示可以传入param和regexp两个参数

 

现在就变成了必须路径带/s并且带参数名wd并且参数值必须是gateway。才允许跳转!

4.3 自定义断言工厂🔥

断言工厂是Spring Cloud Gateway中用于定义路由匹配条件的组件。当一个请求到达网关时,网关会检查请求是否满足路由规则中定义的一个或多个断言条件,只有全部满足时才会将请求路由到目标服务。

断言可以基于请求的各种属性进行匹配,如路径、方法、头部、参数等。Spring Cloud Gateway内置了多种断言工厂,如Path、Method、Header、Cookie等。但有时这些内置断言无法满足特殊业务需求,这时就需要自定义断言工厂。

(1)创建断言工厂类

自定义断言工厂需要继承AbstractRoutePredicateFactory抽象类,并且遵循特定的命名规范:类名必须RoutePredicateFactory结尾。

@Component
public class CustomRoutePredicateFactory extends AbstractRoutePredicateFactory<CustomRoutePredicateFactory.Config> {public CustomRoutePredicateFactory() {super(Config.class);}// 其他必要方法...
}

(2)定义配置类

在断言工厂类内部定义一个静态内部类作为配置类,用于接收和存储断言所需的参数。

@Data
@NoArgsConstructor
public static class Config {private String param;private String value;// 其他配置参数...
}

(3)重写apply方法

apply方法是断言工厂的核心,在这里编写自定义的逻辑来判断请求是否满足断言条件。

@Override
public Predicate<ServerWebExchange> apply(Config config) {return exchange -> {// 自定义判断逻辑// 通过exchange对象可以获取请求的各种信息return true; // 返回是否匹配};
}

(4)重写shortcutFieldOrder方法(可选)

如果希望自定义断言支持快捷配置(在配置文件中使用简写形式),可以重写shortcutFieldOrder方法。

@Override
public List<String> shortcutFieldOrder() {return Arrays.asList("params", "value");
}

4.4 自定义断言工厂示例一

实现一个根据时间范围进行路由的断言工厂,例如只在9:00-18:00允许访问。


/*** 自定义:根据时间范围进行路由的断言工厂* @since 2025/7/8 10:15* @author Mr.Hongtao*/
@Component
public class HourRoutePredicateFactory extends AbstractRoutePredicateFactory<HourRoutePredicateFactory.Config> {/*** 无参构造器,继承父类传入当前的Config类*/public HourRoutePredicateFactory() {super(Config.class);}/*** 自定义断言支持快捷配置(在配置文件中使用简写形式),定义短写法的字段顺序* <p>可选的</p>*/@Overridepublic List<String> shortcutFieldOrder() {return Arrays.asList("startHour", "endHour");}/*** 实现断言逻辑* @param config 配置类* @return 断言*/@Overridepublic Predicate<ServerWebExchange> apply(Config config) {return exchange -> {LocalDateTime now = LocalDateTime.now();int hour = now.getHour();return hour >= config.getStartHour() && hour <= config.getEndHour();};}/*** 内部类:定义可以配置的参数*/@Data@NoArgsConstructorpublic static class Config {private int startHour;private int endHour;}
}

配置使用 

spring:cloud:gateway:routes:- id: three-pageuri: https://www.baidu.com/predicates:- name: Pathargs:pattern: /**- name: Hourargs:start-hour: 9end-hour: 18

测试

我们试试把本地时间调整到这个时段外,然后就发现找不到页面了。(测试完记得改回去)

4.5 自定义断言工厂示例二

实现一个根据请求参数中的年龄值进行路由的断言工厂

@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {public AgeRoutePredicateFactory() {super(Config.class);}@Overridepublic List<String> shortcutFieldOrder() {return Arrays.asList("minAge", "maxAge");}@Overridepublic Predicate<ServerWebExchange> apply(Config config) {return exchange -> {String ageStr = exchange.getRequest().getQueryParams().getFirst("age");if (StringUtils.isNotEmpty(ageStr)) {int age = Integer.parseInt(ageStr);return age > config.getMinAge() && age < config.getMaxAge();}return false;};}@Data@NoArgsConstructorpublic static class Config {private int minAge;private int maxAge;}
}

 

4.6 自定义断言工厂的注意事项

注意事项说明
命名规范类名必须以RoutePredicateFactory结尾,Spring会根据配置中的断言名称自动匹配对应的工厂类。
组件扫描​​确保自定义断言工厂类被Spring组件扫描到,通常使用@Component注解。
配置顺序​​shortcutFieldOrder方法返回的参数顺序必须与配置文件中的参数顺序一致。
线程安全​​断言工厂应该是无状态的,所有的配置应该通过Config类传递。
​​性能考虑断言逻辑应尽量简单高效,因为每个请求都会执行这些判断。

五、过滤器

过滤器可以在请求路由到目标服务之前之后请求响应进行修改、验证或记录,是实现鉴权、限流、日志、请求改写等功能的核心机制。

过滤器执行流程大致如下:

  1. 客户端请求
  2. Pre 过滤器处理
  3. 路由转发
  4. 目标服务处理
  5. Post 过滤器处理
  6. 返回响应

 

以下是官网关于过滤器的文档截图:

5.1 路径重写(RewritePath)过滤🔥

关于过滤器,这里演示一种RewritePath,这是网关经常会用到的,比较多的一种过滤规则。

前面我们说:如果要让网关统一管理不同的微服务,就需要为每个微服务的所有接口都加上各自的路径前缀,也就是说每个请求注解都要加前缀比如/api/order,包括说远程调用客户端也同样要。

这样网关在转发的时候,就能直接到达目的地。

但是这样操作是非常麻烦的,几乎每个接口都要写一遍!

现在提出一种更便捷的办法,就是路径重写

这样我们各个业务模块代码中没有添加路径前缀。但是在网关中统一为各模块配置路径前缀,页面在访问的时候,网关就可以按照过滤器的路径重写规则,找到真实的后端服务地址。

5.2 添加响应头(AddResponseHeader) 过滤

- AddResponseHeader=响应头参数名,参数对应的值

5.3 内置过滤器清单

当然过滤器还有很多,这里就不一一演示了,如下列举了几种过滤器,供大家学习参考:

参数(个数/类型)作用
AddRequestHeader2/string添加请求头
AddRequestHeadersIfNotPresent1/List<String>如果没有则添加请求头, key:value方式
AddRequestParameter2/string, string添加请求参数
AddResponseHeader2/string, string添加响应头
SetStatus1/int设置响应状态码
Retry7/string请求重试设置
RequestSize1/string请求大小限定
RewritePath2/string路径重写
RewriteRequestParameter2/string请求参数重写
RewriteResponseHeader3/string响应头重写

5.4 default-filters

我们如果想给每个服务都使用相同的过滤器,除了在每个服务的routes下都单独配置外,还可以进行统一配置:default-filters,位置与routes是同一级。

spring:gateway:default-filters:- AddResponseHeader=X-Response-Header, Hssy10

5.5 Global Filter

全局过滤器是 Spring Cloud Gateway 提供的一种特殊过滤器,它能够拦截并处理所有的 HTTP 请求和响应。与 GatewayFilter 通过配置定义且处理逻辑固定不同,GlobalFilter 的逻辑需要开发者自己编写代码实现。

(1)实现接口

全局过滤器需要实现 GlobalFilter 接口,该接口只有一个核心方法:

public interface GlobalFilter {Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
  • exchange:请求上下文,可以获取 Request、Response 等信息
  • chain:用来把请求委托给下一个过滤器

(2)基本实现步骤

  1. 创建类实现 GlobalFilter接口
  2. 添加 @Component 注解使其成为 Spring Bean
  3. 通过 @Order 注解或实现 Ordered接口指定执行顺序
  4. 在 filter方法中编写业务逻辑

(3)示例代码

基础认证过滤器

@Component
@Order(-1) // 高优先级
public class AuthGlobalFilter implements GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String token = exchange.getRequest().getHeaders().getFirst("Authorization");if (!"valid-token".equals(token)) {// 认证失败exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();}// 认证通过,继续执行过滤器链return chain.filter(exchange);}
}

疑问:为什么响应头中没有携带上配置文件中定义的响应过滤器?

由于这个全局过滤器直接response响应了,所以配置文件中我们添加的过滤器比如响应头,这些就没有生效了。

带上Authorization请求头看看效果

为了方便演示,我们给edu服务添加一个请求头过滤器看看效果(真实环境肯定不是直接在这配)

说明我们配置的这个全局过滤器生效了。

5.6 自定义过滤器工厂

前面列举了内置的过滤器清单,但是这些过滤器并不能完全满足所有需求。

为了满足有些特殊的需求,于是我们可以通过编写代码来构造一个全局过滤器。

这种方式也可以弥补一定的不足。不过它是全局的,意思是全部模块都要生效的。

下面介绍一种自定义过滤器工厂的方式,这种方式可以像自定义断言工厂一样使用。

即可以用在default-filters上针对全部路由,也可以作用在某个模块上过滤。

(1)继承抽象基类

推荐继承 AbstractGatewayFilterFactory,这是Spring提供的基类,简化了开发流程。类名​必须 GatewayFilterFactory结尾​​,配置中使用前缀部分(如Custom

@Component
public class CustomFilterFactory extends AbstractGatewayFilterFactory<CustomFilterFactory.Config> {// 配置类定义public static class Config {private String param1;private int param2;// getters/setters省略}public CustomFilterFactory() {super(Config.class); // 必须调用父类构造函数}@Overridepublic GatewayFilter apply(Config config) {return (exchange, chain) -> {// 过滤逻辑实现System.out.println("参数1: " + config.param1);return chain.filter(exchange);};}
}

(2)定义配置类

public static class Config {private String key;private String value;// getters/setters
}

(3)重写apply方法

@Overridepublic GatewayFilter apply(Config config) {return (exchange, chain) -> {// 过滤逻辑实现System.out.println("参数1: " + config.param1);return chain.filter(exchange);};}

(4)重写shortcutFieldOrder方法(可选)

@Override
public List<String> shortcutFieldOrder() {return Arrays.asList("key", "value"); // 对应YAML中的参数顺序
}

(5)配置 

filters:- name: Custom  # 自动匹配CustomGatewayFilterFactoryargs:key: X-Custom-Headervalue: GatewayValue

怎么样,是不是发现和自定义断言工厂的方法很像?没错!这里就不一一进行演示了。

5.7 自定义过滤器工厂示例

@Component
public class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthGatewayFilterFactory.Config> {public AuthGatewayFilterFactory() {super(Config.class);}@Overridepublic GatewayFilter apply(Config config) {return new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {return chain.filter(exchange).then(Mono.fromRunnable(() -> {// 每次响应前,添加一个一次性令牌,支持uuid、jwt等各种方式ServerHttpResponse response = exchange.getResponse();HttpHeaders headers = response.getHeaders();String value = config.getValue();if ("uuid".equals(value)) {value = UUID.randomUUID().toString();} else if ("jwt".equals(value)) {// 模拟一串jwtvalue = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI" +"6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";}headers.add(config.getParam(), value);}));}};}@Overridepublic List<String> shortcutFieldOrder() {return Arrays.asList("param", "value");}@Datapublic static class Config {private String param;private String value;}
}

测试

 

六、扩展 - 跨域处理

至此,我们关于gateway的用法其实基本就结束了。

这里扩展一下常见的通过网关处理跨域问题。

以前,我们每个服务可以通过@CrossOrigin或者配置类来解决跨域问题,但是现在我们是微服务,涉及到的服务众多,每个微服务都按以前的方式来搞,也不是不可以。只是个人决定稍微有点麻烦,这里就用网关的方式,统一处理请求跨域。

具体的可以参考官网:CORS Configuration :: Spring Cloud Gateway

七、微服务之间的远程调用经过网关吗?

可以经过网关,也可以不用经过。

比如我们把远程调用的服务名称写成网关的服务名称,然后远程调用的请求路径写网关配置的前缀+路径。这样,远程调用就会走网关。否则远程调用是不经过网关的。

但是,没有必要!

因为网关是直接对接前端的,后端还是老老实实通过各个服务进行调用。

 

http://www.dtcms.com/a/270068.html

相关文章:

  • 人体坐姿检测系统开发实战(YOLOv8+PyTorch+可视化)
  • 本地部署 R 语言环境运行软件 RStudio Server 并实现外部访问
  • 玩具语音方案选型决策OTP vs Flash 的成本功耗与灵活性
  • window wsl 环境下编译openharmony,HarmonyOS 三方库 FFmpeg
  • VLLM 调用有哪些超参数; clean_up_tokenization_spaces是什么
  • ubuntu24.04安装NFS网络文件系统/ARM开发板NFS挂载
  • 20250708-03-string结构及命令详解_笔记
  • CI/CD — DevOps概念之实现k8s持续交付持续集成(一)
  • NumPy-统计函数详解
  • UE5内置插件 AnimToTexture 简单入门
  • 一次编码,多端运行:HTML5多终端调用
  • Android 依赖注入框架详解
  • 2025年7月8日学习笔记——模式识别与机器学习绪论
  • bro code Interface
  • 社区云管家 - 智慧生活新方式 ——仙盟创梦IDE
  • 玩转Docker | 使用Docker部署HomeBox家庭库存管理工具
  • NestJS 系列教程(四):中间件、中断器与异常过滤器详解
  • JavaScript基础篇——第一章 JavaScript基础的认识
  • MySQL 【环境安装、基础认识】
  • OpenCV在Visual Studio 2022下的配置
  • 牛市来临之际,如何用期权抢占反弹先机?
  • Centos和麒麟系统如何每天晚上2点10分定时备份达梦数据库
  • VUE 带有搜索功能的穿梭框(简单demo)
  • 【DOCKER】-2 docker基础
  • 科技云报到:云智融合双buff,AI已开挂
  • 数据结构-顺序表-拿硬币
  • Effective Python 条款5 用辅助函数取代复杂的表达式
  • 数据结构:位图
  • 【保姆级目标检测教程】Ubuntu 20.04 部署 YOLOv13 全流程(附训练/推理代码)
  • Rust DevOps框架管理实例