Spring Cloud Gateway 快速入门:路由、断言、过滤器一次搞懂

Spring Cloud Gateway 快速入门:路由、断言、过滤器一次搞懂
目录① 导读卡片② 背景与目标为什么学学完能怎样③ 概念与原理一句话概括三大核心概念底层跑的是 Reactive响应式编程④ 逻辑与对比Gateway vs Zuul 1.x技术选型建议⑤ 核心详解5.1 快速搭建第一步Maven 依赖第二步YAML 配置路由5.2 Predicate断言请求匹配器5.3 Filter过滤器请求/响应处理器内置过滤器一览过滤器的执行顺序5.4 自定义过滤器方式一实现 GatewayFilterFactory推荐可配置方式二实现 GlobalFilter全局过滤器所有路由生效5.5 完整路由配置示例⑥ 案例实战案例微服务网关 认证 路径重写⑦ 避坑 最佳实践❌ 常见错误错误 1Gateway 和 spring-boot-starter-web 冲突错误 2lb:// 协议但没注册中心错误 3过滤器中修改 Response 后忘记 setComplete()错误 4Predicate 写错导致路由不匹配✅ 最佳实践清单⑧ 总结 路线图记住了什么执行顺序口诀下一步去哪① 导读卡片项目内容一句话定位从零掌握 Spring Cloud Gateway 的三大核心概念Route、Predicate、Filter理解 Reactive 过滤器链的执行顺序能写出自定义过滤器适合人群Java 微服务开发者、Spring Cloud 使用者、API 网关需求方难度⭐⭐⭐中等需了解微服务和响应式编程基本概念阅读时长18 分钟前置知识Spring Boot 基础、微服务概念、HTTP 请求理解② 背景与目标为什么学微服务架构中你需要一个统一的入口来处理请求路由到不同的后端服务统一的认证鉴权请求/响应日志记录限流与熔断跨域处理Spring Cloud Gateway 就是干这个的。它基于Spring WebFlux响应式编程比 Zuul 1.x 性能更好、更轻量。学完能怎样✅ 理解 Gateway 的三大核心概念Route、Predicate、Filter✅ 能用 YAML 配置路由规则✅ 能写自定义过滤器✅ 理解 Reactive 过滤器链的执行顺序③ 概念与原理一句话概括Gateway 路由器 拦截器链请求进来 → 匹配路由规则 → 经过过滤器链处理 → 转发到目标服务 → 响应原路返回。三大核心概念概念类比作用Route路由快递单定义转发规则谁来 → 上哪去Predicate断言分拣条件判断请求是否匹配某条路由Filter过滤器打包/拆包在转发前后对请求/响应做处理底层跑的是 Reactive响应式编程Gateway 基于 Spring WebFlux使用非阻塞 I/O和Reactor 库。过滤器链不是同步阻塞的而是通过回调机制// 传统的 Servlet Filter同步阻塞 public void doFilter(req, res, chain) { // 前置 chain.doFilter(req, res); // 阻塞等待 // 后置 } ​ // Gateway Filter响应式非阻塞 public MonoVoid filter(exchange, chain) { // 前置 return chain.filter(exchange) // 不阻塞返回 Mono .then(Mono.fromRunnable(() - { // 注册回调 // 后置响应回来后执行 })); }两种写法的逻辑完全一样只是 Reactive 用回调代替了同步阻塞。④ 逻辑与对比Gateway vs Zuul 1.x对比维度Spring Cloud GatewayZuul 1.x底层WebFluxReactive、非阻塞Servlet同步阻塞性能更高非阻塞 I/O较低线程池模型长连接原生支持 WebSocket不支持编程模型Reactor 响应式传统 ServletSpring Boot 兼容2.x1.x / 2.x技术选型建议场景推荐新项目 Spring Boot 2.x✅Gateway老项目迁移✅ Gateway但注意 WebFlux 与 Servlet 冲突必须用 Servlet 生态❌ 考虑 Zuul 2.x 或 Spring Cloud LoadBalancer⑤ 核心详解5.1 快速搭建第一步Maven 依赖dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-gateway/artifactId /dependency ​ !-- 注意不需要 spring-boot-starter-web -- !-- Gateway 基于 WebFlux与 spring-boot-starter-web 冲突 --第二步YAML 配置路由server: port: 8080 ​ spring: cloud: gateway: routes: # 路由 1用户服务 - id: user-service # ① 路由唯一标识 uri: http://localhost:8081 # ② 转发到哪 predicates: # ③ 匹配条件 - Path/user/** filters: # ④ 拦截处理 - AddRequestHeaderX-Request-Id, 123 ​ # 路由 2订单服务 - id: order-service uri: http://localhost:8082 predicates: - Path/order/** filters: - StripPrefix1 # /api/order/list → /order/list5.2 Predicate断言请求匹配器Predicate 的本质是一个ServerWebExchange → boolean的函数判断请求是否匹配。predicates: # ---- 路径匹配 ---- - Path/user/**, /admin/** # ---- 请求头匹配支持正则 ---- - HeaderX-Request-Source, \d # ---- 查询参数匹配 ---- - Queryage, \d # 有 age 参数且是数字 # ---- HTTP 方法匹配 ---- - MethodGET, POST # ---- Cookie 匹配 ---- - CookiesessionId, . # ---- 请求时间范围 ---- - Between2024-01-01T00:00:0008:00, 2024-12-31T23:59:5908:00多断言间是 AND 关系必须全部满足才路由predicates: - Path/order/** - MethodPOST # 必须是 POST 且路径以 /order 开头5.3 Filter过滤器请求/响应处理器内置过滤器一览filters: # ═══ 请求修改 ═══ - AddRequestHeaderX-Trace-Id, ${uuid} # 添加请求头 - AddRequestParametersource, gateway # 添加查询参数 - AddResponseHeaderX-Gateway, v1 # 添加响应头 # ═══ 路径重写 ═══ - StripPrefix1 # /api/user/list → /user/list - PrefixPath/api # /user/list → /api/user/list - RewritePath/foo/(?seg.*), /$\{seg} # 正则路径重写 # ═══ 限流 ═══ - name: RequestRateLimiter args: key-resolver: #{userKeyResolver} redis-rate-limiter.replenishRate: 10 redis-rate-limiter.burstCapacity: 20 # ═══ 熔断 ═══ - name: CircuitBreaker args: name: myCircuitBreaker fallbackUri: forward:/fallback # ═══ 重试 ═══ - name: Retry args: retries: 3 statuses: BAD_GATEWAY过滤器的执行顺序filters: - Timer # ① 计时 - Auth # ② 鉴权 - RateLimit # ③ 限流 - StripPrefix1 # ④ 路径处理执行流程Timer前置 → Auth前置 → RateLimit前置 → StripPrefix前置 → 后端服务 │ Timer后置 ← Auth后置 ← RateLimit后置 ← StripPrefix后置 ←────┘像洋葱一样先一层层往里进前置到底了再一层层出来后置。5.4 自定义过滤器方式一实现 GatewayFilterFactory推荐可配置/** * 计时过滤器记录请求处理耗时 * 在 yaml 中配置filters: - Timer */ Component public class TimerGatewayFilterFactory extends AbstractGatewayFilterFactoryObject { public TimerGatewayFilterFactory() { super(Object.class); // 不需要额外配置 } Override public GatewayFilter apply(Object config) { return new TimerFilter(); } private static class TimerFilter implements GatewayFilter { Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { // ══════ 前置记开始时间 ══════ long start System.currentTimeMillis(); String path exchange.getRequest().getPath().value(); System.out.println([前置] 请求来了: path); // ══════ 放行交给下一个过滤器 ══════ return chain.filter(exchange) .then(Mono.fromRunnable(() - { // ══════ 后置响应回来再执行 ══════ long cost System.currentTimeMillis() - start; System.out.println([后置] path 耗时: cost ms); })); } } }方式二实现 GlobalFilter全局过滤器所有路由生效/** * JWT 认证全局过滤器 * 对所有经过 Gateway 的请求做 token 校验 */ Component Order(-1) // 数值越小优先级越高-1 表示最早执行 public class JwtAuthGlobalFilter implements GlobalFilter { Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 放行登录/注册接口 String path exchange.getRequest().getPath().value(); if (path.contains(/login) || path.contains(/register)) { return chain.filter(exchange); } // 校验 Token String token exchange.getRequest() .getHeaders().getFirst(Authorization); if (token null || !token.startsWith(Bearer )) { // 认证失败 → 直接返回 401 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); exchange.getResponse().getHeaders() .setContentType(MediaType.APPLICATION_JSON); return exchange.getResponse().setComplete(); } // 校验通过 → 放行 return chain.filter(exchange); } }5.5 完整路由配置示例spring: cloud: gateway: routes: # ── 用户服务 ── - id: user-service uri: lb://user-service # lb:// 表示从 Nacos/Eureka 服务发现 predicates: - Path/user/** filters: - StripPrefix0 - AddRequestHeaderX-Gateway-Source, gateway # ── 订单服务 ── - id: order-service uri: lb://order-service predicates: - Path/order/** filters: - StripPrefix0 - name: RequestRateLimiter args: key-resolver: #{ipKeyResolver} redis-rate-limiter.replenishRate: 10 redis-rate-limiter.burstCapacity: 20 - name: Retry args: retries: 3 statuses: SERVICE_UNAVAILABLE⑥ 案例实战案例微服务网关 认证 路径重写// App.java 启动类 SpringBootApplication EnableDiscoveryClient public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } # application.yml server: port: 8080 spring: application: name: gateway-service cloud: nacos: discovery: server-addr: 127.0.0.1:8848 gateway: routes: # 用户服务 - id: user-service uri: lb://user-service predicates: - Path/api/user/** filters: - StripPrefix1 # /api/user/list → /user/list # 订单服务 - id: order-service uri: lb://order-service predicates: - Path/api/order/** filters: - StripPrefix1 # 全局默认过滤器 default-filters: - AddResponseHeaderX-Gateway-Version, 1.0.0 // 自定义认证全局过滤器 Component Order(-1) public class AuthGlobalFilter implements GlobalFilter { // 白名单路径不需要认证 private static final ListString WHITE_LIST Arrays.asList( /user/login, /user/register ); Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { String path exchange.getRequest().getPath().value(); System.out.println([Gateway] 请求路径: path); // 白名单放行 for (String whitePath : WHITE_LIST) { if (path.contains(whitePath)) { return chain.filter(exchange); } } // 校验 token String token exchange.getRequest() .getHeaders().getFirst(Authorization); if (StringUtils.isEmpty(token)) { return unauthorized(exchange, 缺少认证信息); } // 这里可以调用认证服务验证 token // 简单演示token 以 valid- 开头即通过 if (!token.startsWith(valid-)) { return unauthorized(exchange, Token 无效); } return chain.filter(exchange); } private MonoVoid unauthorized(ServerWebExchange exchange, String msg) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); exchange.getResponse().getHeaders() .setContentType(MediaType.APPLICATION_JSON); byte[] bytes ({\code\:401,\msg\:\ msg \}) .getBytes(StandardCharsets.UTF_8); return exchange.getResponse() .writeWith(Mono.just(exchange.getResponse() .bufferFactory().wrap(bytes))); } }⑦ 避坑 最佳实践❌ 常见错误错误 1Gateway 和 spring-boot-starter-web 冲突!-- ❌ 错误同时引入会导致启动报错 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId !-- 不能有 -- /dependency dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-gateway/artifactId /dependency !-- ✅ 正确只引入 Gateway -- dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-gateway/artifactId /dependencyGateway 基于 WebFlux和 Spring MVC 共用 Tomcat 会冲突。错误 2lb:// 协议但没注册中心# ❌ 错误uri: lb://user-service 需要 Nacos/Eureka # 没有注册中心时会报错No instances available for user-service # ✅ 没有注册中心时用 http:// uri: http://localhost:8081 # ✅ 有注册中心时用 lb:// uri: lb://user-service错误 3过滤器中修改 Response 后忘记setComplete()// ❌ 错误设置了状态码但没终止请求 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return chain.filter(exchange); // 继续执行 → 返回正常响应 // ✅ 正确设置状态码后终止请求链 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); // 终止不继续执行错误 4Predicate 写错导致路由不匹配# ❌ 错误想匹配 /user/xxx 但 Path 写成了 /user/* # 实际上是 /user/** 才能匹配多级路径 predicates: - Path/user/* # ❌ 只匹配 /user/xxx不匹配 /user/xxx/yyy # ✅ 正确 predicates: - Path/user/** # ✅ 匹配 /user/xxx 和 /user/xxx/yyy✅ 最佳实践清单实践说明RestTemplate 换成 WebClientGateway 环境没有 Servlet 容器自定义过滤器用 GatewayFilterFactory可配置、可复用全局鉴权用 GlobalFilter所有路由自动生效路径重写用 StripPrefix / RewritePath解耦前端路径和后端路径Predicate 多条件用 AND所有断言必须同时满足才路由过滤器优先级用 Order数值越小越先执行⑧ 总结 路线图记住了什么概念一句话总结Route路由规则匹配条件 目标地址 过滤器Predicate断言匹配器if(条件) → 走这个路由Filter过滤器前置处理 → 放行 → 后置处理Reactivechain.filter().then(后置回调)非阻塞 I/O执行顺序口诀路由匹配 → 前置过滤器 → 转发后端 → 后置过滤器 → 返回响应下一步去哪Gateway 动态路由从 Nacos / 数据库动态更新路由配置Gateway Sentinel 限流基于 Sentinel 的网关限流Gateway Spring Security统一认证授权Gateway WebSocket 支持路由 WebSocket 请求