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

分布式微服务--GateWay(过滤器及使用Gateway注意点)

前言、Spring Cloud Gateway 与 Web 依赖冲突

    <!-- 下面两个依赖不能同时使用   --><!-- Gateway 组件 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId><version>2.0.0.RELEASE</version></dependency><!-- springboot-web组件 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>

1. 现象

  • 在使用 spring-cloud-starter-gateway 时,如果项目同时引入了 spring-boot-starter-web(传统的 Servlet Web 依赖),两者会冲突。

  • 主要表现为:启动时端口被占用、DispatcherServlet 与 Netty 服务器冲突,导致服务无法正常启动。

2. 原因

  • Spring Cloud Gateway 基于 Spring WebFlux,使用的是 Netty 作为底层服务器(响应式非阻塞)。

  • spring-boot-starter-web 是基于传统的 Servlet,使用的是 Tomcat 容器(阻塞模型)。

  • 两种模型底层不兼容,不能同时启动。

3. 解决方案

  • 如果要使用 Gateway,项目中应使用 spring-boot-starter-webflux 替代 spring-boot-starter-web

  • 保证只用一个 Web 服务器容器(Netty)。

  • 不能同时引入 spring-boot-starter-webspring-cloud-starter-gateway

4. 注意事项

  • 如果项目有控制器需要兼容传统 MVC,推荐拆分服务:

    • API 网关用 Spring Cloud Gateway + WebFlux

    • 后端服务用 Spring MVC (spring-boot-starter-web)

  • 避免同时使用两个 Web 依赖。

一、三种路由配置方式详解与对比:自动发现、静态 URI 与服务名路由(lb)

✅ 1、三种 Gateway 配置方式说明

1️⃣ 自动服务发现路由(基于注册中心,比如 Nacos)

spring:cloud:gateway:discovery:locator:enabled: true

✅ 说明:

  • 开启后 Gateway 会自动将注册中心(如 Nacos)中注册的服务生成路由。

  • 不需要手动写 routes

  • 访问路径格式固定为:http://网关地址/服务名/xxx

  • 实际转发到的服务为注册中心中对应服务实例

✅ 适用场景:

  • 微服务较多,想简化网关配置。

  • 不需要定制复杂路由规则。

✅ 是否用到 Nacos:

✔ 是的,必须使用 Nacos / Eureka 等注册中心,才能自动发现服务。


2️⃣ 手动配置路由(静态 URI)

spring:cloud:gateway:routes:- id: baiduuri: http://www.baidu.com/predicates:- Path=/baidu/**

✅ 说明:

  • 手动指定目标 URI。

  • uri 是静态地址,不能做服务发现。

  • Path=/baidu/** 表示匹配 /baidu/* 的请求会被转发到 http://www.baidu.com

✅ 适用场景:

  • 转发到外部服务,如第三方接口、静态页面。

  • 不使用服务发现(非微服务调用)。

✅ 是否用到 Nacos:

❌ 不需要 Nacos。


3️⃣ 手动配置路由(基于服务名,使用负载均衡)

 基于服务名,就必须用lb:

spring:cloud:gateway:routes:- id: user-serviceuri: lb://user-service/predicates:- Path=/user/**

✅ 说明:

  • lb://user-service 表示通过 注册中心获取 user-service 实例,并使用负载均衡访问

  • lb: 是 load balancer 的前缀,Spring Cloud Gateway 会自动接入负载均衡策略。

✅ 适用场景:

  • 想要自定义路由规则 + 使用 Nacos 服务发现。

  • 比如 /user/** 映射到 user-service 微服务。

✅ 是否用到 Nacos:

✔ 是的,需要注册中心。

注意:

        Spring Cloud Gateway 使用的是 Spring Cloud LoadBalancer,默认策略是 RoundRobinLoadBalancer(轮询算法)。如果想使用随机就去定义下列代码即可

@Configuration
public class ProducerLoadBalancerConfig {@Beanpublic ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,LoadBalancerClientFactory factory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);//随机return new RandomLoadBalancer(factory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);//轮询return new RoundRobinLoadBalancer(factory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);return new MyCustomLoadBalancer(); // 自定义负载均衡策略}
//也可以自定义负载均衡策略
public class MyCustomLoadBalancer implements ReactorServiceInstanceLoadBalancer {private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;private final String serviceId;public MyCustomLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> provider, String serviceId) {this.serviceInstanceListSupplierProvider = provider;this.serviceId = serviceId;}@Overridepublic Mono<Response<ServiceInstance>> choose(Request request) {return serviceInstanceListSupplierProvider.getIfAvailable().get().next().map(serviceInstances -> {// TODO: 在这里写你的选择逻辑(比如权重、元数据等)if (serviceInstances.isEmpty()) {return new EmptyResponse();}// 简单示例:随机ServiceInstance instance = serviceInstances.get(ThreadLocalRandom.current().nextInt(serviceInstances.size()));return new DefaultResponse(instance);});}
}}

  想了解更多负载均衡以及如果我不同服务想用不同负载均衡策略怎么办?

        可以去看笔者的这两篇博客这里不多赘述

分布式微服务--万字详解 微服务的各种负载均衡全场景以注意点-CSDN博客

分布式微服务--Ribbon 与 Spring Cloud LoadBalancer 区别 (五)-CSDN博客


✅ 2、三种配置方式对比总结

配置方式依赖 Nacos是否动态发现路由配置方式场景
discovery.locator.enabled✅ 是✅ 自动发现服务不写 routes微服务多,简化配置
routes + 静态 URI(http://)❌ 否❌ 静态目标手动写 routes转发外部接口
routes + 动态 URI(lb://)✅ 是✅ 使用服务名手动写 routes自定义规则 + 服务发现

✅ 3、常见配置误区提醒

  • 开启 discovery.locator.enabled: true 后,如果你写了 routes,它们可以 共存,但 routes 优先级更高

  • lb://xxx 必须启用服务发现并引入 spring-cloud-starter-loadbalancer 或等效功能(大多数 starter 默认引入)。

  • 若使用 Nacos,需要引入如下依赖:

<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

二、uri中结尾加不加/的区别

🌐 Gateway 路由中 uri 是否加 / 的影响

📌 背景示例

spring:cloud:gateway:routes:- id: baiduuri: http://www.baidu.compredicates:- Path=/baidu/**

访问 http://localhost:8080/baidu/index 时:

配置方式实际转发地址
uri: http://www.baidu.com(无 /http://www.baidu.com/baidu/index
uri: http://www.baidu.com/(有 /http://www.baidu.com/index

✅ 原因说明

  • 请求 /baidu/index 命中路由,剩余路径会拼接到 uri 后。

  • 如果 uri 没有 /,会直接拼接整个 /baidu/index

  • 如果 uri/,则拼接去掉前缀后的部分(更合理)。


🛠 推荐写法(最佳实践)

使用 StripPrefix 过滤器去除前缀:

spring:cloud:gateway:routes:- id: baiduuri: http://www.baidu.com/predicates:- Path=/baidu/**filters:- StripPrefix=1

效果

  • 请求 /baidu/index

  • 实际转发:http://www.baidu.com/index


📝 总结建议

配置项建议用法说明
uri结尾加 /避免重复路径拼接
StripPrefix根据路径前缀设置去掉前缀,保持后端路径整洁

注意:为什么看起来uri结尾有/时,加不加filters:- StripPrefix=1,效果一样呢?

在这个例子中,两种配置的转发结果一致,是因为:

  • uri 带斜杠时,Gateway 的默认行为就是去除断言中定义的前缀(/baidu);
  • StripPrefix=1 在这里的作用与默认行为重合(都是去除 /baidu)。

在下面例子就有所不同了

例:多段前缀(如 /api/baidu/**

predicates:- Path=/api/baidu/**# 无 StripPrefix + uri 带斜杠:去除完整前缀 /api/baidu,转发到 http://www.baidu.com/s?wd=test
# 有 StripPrefix=1 + uri 带斜杠:仅去除第一段 /api,转发到 http://www.baidu.com/baidu/s?wd=test

总结

  • 在你的特定例子中,两种配置效果表面相同(转发地址一致);
  • 但本质逻辑不同:前者依赖 uri 带斜杠的默认行为,后者依赖显式的 StripPrefix 过滤器;
  • 最佳实践:推荐使用 StripPrefix 过滤器,因为它更直观、可控,避免依赖 uri 斜杠的隐式规则(尤其在复杂路径场景下)。

🧪 三、支持的断言(Predicates)常用类型

Predicate示例说明
Path/user/**请求路径匹配
MethodGET, POST请求方法匹配
Header"X-Request-Id", "^\d+$"请求头匹配
Host**.example.comHost 匹配
After/Before/Between时间限制访问支持 ISO8601 时间格式

🧩 四、支持的过滤器(Filters)常用类型

Filter示例说明
AddRequestHeaderAddRequestHeader=X-Request-color, blue添加请求头
AddResponseHeader同上添加响应头
RewritePathRewritePath=/foo/(?<segment>.*), /$\{segment}路径重写
StripPrefixStripPrefix=1去掉路径前缀
Hystrixname=myHystrixCommand fallbackUri=forward:/fallback熔断处理(Spring Cloud Netflix 需要依赖)

过滤器小例子

        下面的代码通过A使用服务名和Gateway调用到B的时候,会在这个请求添加请求头并且B中可以获取这个请求头的值

调用方A:(只需要在yml中添加 AddRequestHeader=X-Request-color,red)

server:port: 7009
spring:application:name: boyatop-gatewaycloud:nacos:discovery:server-addr: 127.0.0.1:8848gateway:routes:- id: memberuri: lb://member1predicates:- Path=/member/**filters:  #下面是过滤器的配置- StripPrefix=1- AddRequestHeader=X-Request-color,red #添加请求头

被调用方B:

//yml
server:port: 8081
spring:application:name: membercloud:nacos:discovery:server-addr: 127.0.0.1:8848
//启动类
@SpringBootApplication
public class MemberApplication {public static void main(String[] args) {SpringApplication.run(MemberApplication.class, args);}
}
//Controller类
@RestController
@Slf4j
public class headerController{@RequestMapping("/get")public String get(){return "hhh8081";}@GetMapping("/testgateway")public String testGateway(HttpServletRequest request) throws Exception {log.info("gateWay获取请求头X-Request -color:"   +request.getHeader("X-Request-color"));return "success";}@GetMapping("/testgateway2")public String testGateway(@RequestHeader("X-Request-color") String color) throws Exception {log.info("gateWay获取请求头X-Request -color:"+color);return "success";}
}

🔍五、自定义过滤器(GlobalFilter)

一、两者概念区别

对比项自定义过滤器(GatewayFilter)全局过滤器(GlobalFilter)
作用范围只作用于配置中的指定路由作用于所有请求(全局)
实现接口GatewayFilter + AbstractGatewayFilterFactoryGlobalFilter + Ordered
是否需要 yml 配置✅ 是,必须在 application.yml 的路由中配置❌ 否,不需要配置,自动生效
应用场景针对某个路由的参数处理、Header 增加等鉴权、日志、异常统一处理、限流等
触发机制仅当匹配到某条路由才执行每个请求都会执行

二、自定义过滤器(AbstractGatewayFilterFactory)示例

1️⃣ 编写自定义过滤器类:

@Component
public class AddHeaderGatewayFilterFactory extends AbstractGatewayFilterFactory<AddHeaderGatewayFilterFactory.Config> {public static class Config {private String name;private String value;// getter / setter}public AddHeaderGatewayFilterFactory() {super(Config.class);}@Overridepublic GatewayFilter apply(Config config) {return (exchange, chain) -> {// 添加请求头exchange.getRequest().mutate().header(config.name, config.value).build();return chain.filter(exchange);};}
}

2️⃣ yml 中使用该过滤器:

spring:cloud:gateway:routes:- id: user_routeuri: lb://user-servicepredicates:- Path=/user/**filters:#这个名称必须与AddHeaderGatewayFilterFactory所匹配否则匹配不上#也就是说自定义拦截器叫HhGatewayFilterFactory#下面也要写成Hh=name,value - AddHeader=name,value 

3. 自定义过滤器和全局过滤器使用场景对比

需求 / 功能推荐使用类型说明
针对某个接口改请求头、参数自定义过滤器精准控制某条路由
日志记录(所有请求)全局过滤器统一处理
全局 token 鉴权全局过滤器所有路由都校验
自定义 header 注入自定义过滤器某个接口单独处理
异常处理 / 限流全局过滤器通用逻辑

🔍五、全局过滤器(GlobalFilter)

✅ 不需要写在 yml 中,所有请求默认都会走这个过滤器。

@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String token = exchange.getRequest().getHeaders().getFirst("token");if (StringUtils.isEmpty(token)) {exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();}return chain.filter(exchange);}@Overridepublic int getOrder() {return -1; // 越小越先执行}
}

这个全局过滤器实现了:

  • 检查请求头中是否包含 token

  • 如果没有,直接返回 401 状态码

✅ 实现 Ordered接口

5.1、核心区别
是否实现 Ordered 接口执行顺序可控说明
❌ 未实现❌ 不可控(默认顺序)无法确定具体执行顺序
✅ 实现✅ 可控可通过 getOrder() 指定优先级

5.2、Ordered 接口作用

Ordered 是 Spring 提供的一个接口:

  • 用于定义组件(如 Filter、Interceptor 等)的执行顺序

  • 数字越小,优先级越高(越早执行)


5.3、代码示例对比
5.3.1 不实现 Ordered(默认顺序)
@Component
public class AuthGlobalFilter implements GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 过滤逻辑return chain.filter(exchange);}
}
  • 执行顺序默认;

  • 多个过滤器顺序无法手动控制。


5.3.2 实现 Ordered(手动控制顺序)
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 过滤逻辑return chain.filter(exchange);}@Overridepublic int getOrder() {return -1; // 数字越小,优先级越高}
}
  • 可以明确控制执行顺序;

  • 常用于权限验证、日志记录等需要优先执行的逻辑。


5.4、getOrder() 的优先级说明
数值优先级说明常用场景
-1高优先级权限校验
0默认优先级日志记录等常规
1比默认稍晚后置处理
100明显靠后最后处理逻辑

✅ 实践建议:
  • 需要控制顺序 ➜ 实现 Ordered

  • 不关心顺序 ➜ 可不实现 Ordered

  • 权限校验类 Filter ➜ 推荐 getOrder() = -1 或更小


📄六、断言与过滤器

6.1  一句话区分:

  • 断言(Predicate):判断「是否进入」某条路由。

  • 过滤器(Filter):在进入或返回时「做点处理」。

6.2 更通俗的比喻:

把 Gateway 想象成一个大门口

  • 断言 = 门口的保安:检查条件是否满足,比如:路径是不是 /user/**,IP 是不是白名单,如果不符合,就不让进。

  • 过滤器 = 安检员+引导员:你进门之后,它检查你有没有带违禁品(请求修改),或者走的时候送你一张发票(响应修改)。


6.3 什么时候用哪个?
作用用断言用过滤器
控制哪些请求能进来✅ 是的,比如判断路径、方法、参数等❌ 不负责判断入口
修改请求内容(如添加请求头)❌ 做不到✅ 过滤器可以
修改响应内容(如统一返回格式)❌ 做不到✅ 过滤器可以
鉴权(Token 检查)❌ 不负责✅ 通常放过滤器中
路由转发前的数据加工❌ 不处理✅ 可以改路径、改头信息等

🧰 七、常见问题及排查技巧

问题解决方式
请求不转发检查 routes 中的 path 和 uri 是否正确
路径不匹配加上 StripPrefix 或 RewritePath
服务名不识别使用 Nacos 并开启服务发现
过滤器无效全局:实现 GlobalFilter;局部:添加在路由 filters 中
请求头 token 丢失加日志观察请求流转路径;部分服务可能会过滤头部字段

🧠 八、总结

  • Spring Cloud Gateway 是基于 WebFlux 的高性能网关组件

  • 推荐在微服务中作为 API 网关统一入口使用

  • 配置灵活,可通过 YML 实现路径转发、负载均衡

  • 支持断言(匹配规则)和过滤器(增强功能)

  • 可结合 Nacos、OAuth2 等组件构建更复杂的网关服务

http://www.lryc.cn/news/613065.html

相关文章:

  • 告别YAML,在SpringBoot中用数据库配置替代配置文件
  • word生成问题总结
  • 【遥感图像入门】近三年遥感图像建筑物细粒度分类技术一览
  • Day116 若依融合mqtt
  • 界面组件DevExpress WPF中文教程:网格视图数据布局 - 紧凑模式
  • 音视频时间戳获取与同步原理详解
  • 【Docker】RustDesk远程控制-私有化部署开源版本
  • 生成式AI的“幽灵漏洞”:法律如何为技术的阴影划界
  • PCIe Base Specification解析(八)
  • 从配置到远程访问:如何用群晖NAS FTP+ Cpolar搭建稳定文件传输通道
  • 深入解析Three.js中的BufferAttribute:源码与实现机制
  • Linux下动态库链接的详细过程
  • C++位图(Bitmap)与布隆过滤器(Bloom Filter)详解及海量数据处理应用
  • vue3父组件把一个对象整体传入子组件,还是把一个对象的多个属性分成多个参数传入
  • C#中统计某个字符出现次数的最简单方法
  • Git `cherry-pick` 工具汇总
  • Numpy科学计算与数据分析:Numpy线性代数基础与实践
  • 第一个vue应用
  • 【Kubernetes】部署 kube-bench 实现 K8s 最佳实践
  • LeetCode 分类刷题:125. 验证回文串
  • LongVie突破超长视频生成极限:1分钟电影级丝滑视频,双模态控制告别卡顿退化
  • Postman接口测试入门
  • ESXI7.0添加标准交换机过程
  • Python 位置参数(positional arguments)
  • 大文件断点续传(vue+springboot+mysql)
  • 8.结构健康监测选自动化:实时数据 + 智能分析,远超人工
  • Python 基础详解:变量(Variables)—— 程序的“记忆单元”
  • Numpy科学计算与数据分析:Numpy数据分析基础之统计函数应用
  • 理清C语言中动态内存管理相关函数
  • 思科设备密码恢复方法