当前位置: 首页 > 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.dtcms.com/a/319679.html

相关文章:

  • 翻译模型(TM):基于短语的统计翻译模型(PBSMT)的构建
  • C++语法与面向对象特性(2)
  • PyTorch如何实现婴儿哭声检测和识别
  • 目标检测数据集 - 自动驾驶场景道路异常检测数据集下载「包含VOC、COCO、YOLO三种格式」
  • 接口自动化-pytest
  • OpenAI 开源模型 gpt-oss 正式上线微软 Foundry 平台
  • 网络可视,运维无忧:分钟级定位,告别盲目扩容
  • 从零开始构建情绪可视化日记平台 - React + TypeScript + Vite
  • CPTS Remote 复现
  • Redisson中的分布式锁
  • 自动化办公革命:3小时完成8小时工作量
  • 钢卷矫平机科普:把“弯曲的记忆”清零
  • 人工智能与能源:AI 驱动的能源产业变革
  • MCU-基于TC397的双BootLoader设计方案
  • 关于vue2中对接海康摄像头以及直播流rtsp或rtmp,后台ffmpeg转码后通过ws实现
  • 【linux】vmware中ubuntu无法上网
  • 关于 cadence导入原理图出现ERROR(ORCAP-1192)错误 的解决方法
  • 蓝桥杯算法之搜索章 - 3
  • HarmonyOS分布式开发实战:打造跨设备协同应用
  • C 语言主控开发与显控开发能力体系及技术栈详解,STM32、QT、嵌入式、边缘系统显示
  • 【Vite】Vite 构建 React 项目中 Select a variant 配置选择指南:标准版 vs SWC
  • shell脚本while只循环一次,后续循环失效
  • 解码算法:维特比算法(Viterbi)在SMT中的应用
  • 开发避坑指南(20) :MyBatis操作Oracle插入NULL值异常“无效列类型1111“解决方案
  • 金仓KingbaseES逻辑架构,与Oracle/MySQL对比
  • Windows Oracle 11 g dmp数据库恢复笔记
  • 一种基于CEEMDAN-小波阈值联合降噪-快速谱峭度(FSK)/基尼谱Ginigram/Autogram的故障诊断 Matlab
  • 【已解决】-bash: mvn: command not found
  • [Oracle] FLOOR()函数
  • Oracle 12c + Pl/Sql windows系统下表空间创建、迁移,dmp备份导入,数据库字符集更改