Spring Cloud Gateway 微服务网关 设计思想与深度解析

基于:Spring Cloud Gateway 5.0.x(最新稳定版 5.0.1,对应 Spring Boot 4.0.x / Spring Cloud 2025.1 Oakwood);4.2.x/4.3.x 用法兼容(Spring Boot 3.4/3.5)

Spring Cloud Gateway 的定位与核心概念(Route / Predicate / Filter)、响应式架构设计思想、请求处理链路源码级剖析、完整使用实战(配置式/编程式/动态路由/限流熔断),以及与 Zuul / Nginx 的选型对比。


目录

  1. 核心理论:网关是什么
    • [1.1 从痛点到定位](#1.1 从痛点到定位)
    • [1.2 三大核心概念:Route / Predicate / Filter](#1.2 三大核心概念:Route / Predicate / Filter)
    • [1.3 与 Zuul / Nginx 对比选型](#1.3 与 Zuul / Nginx 对比选型)
    • [1.4 版本体系与兼容矩阵](#1.4 版本体系与兼容矩阵)
  2. 设计思想与核心机制
    • [2.1 基于 WebFlux 的响应式架构](#2.1 基于 WebFlux 的响应式架构)
    • [2.2 请求处理链路总览](#2.2 请求处理链路总览)
    • [2.3 路由定位机制](#2.3 路由定位机制)
    • [2.4 过滤器链执行机制](#2.4 过滤器链执行机制)
    • [2.5 内置断言工厂与过滤器工厂](#2.5 内置断言工厂与过滤器工厂)
    • [2.6 负载均衡与动态路由](#2.6 负载均衡与动态路由)
    • [2.7 限流机制:Redis 令牌桶](#2.7 限流机制:Redis 令牌桶)
    • [2.8 熔断降级集成(Resilience4j)](#2.8 熔断降级集成(Resilience4j))
    • [2.9 Gateway 5.0 新特性](#2.9 Gateway 5.0 新特性)
  3. 使用实战
    • [3.1 快速接入:第一个路由](#3.1 快速接入:第一个路由)
    • [3.2 配置式路由详解](#3.2 配置式路由详解)
    • [3.3 Java DSL 编程式路由](#3.3 Java DSL 编程式路由)
    • [3.4 动态路由(Nacos 配置中心)](#3.4 动态路由(Nacos 配置中心))
    • [3.5 自定义全局过滤器:统一鉴权](#3.5 自定义全局过滤器:统一鉴权)
    • [3.6 限流实战(Redis RateLimiter)](#3.6 限流实战(Redis RateLimiter))
    • [3.7 熔断与重试配置](#3.7 熔断与重试配置)
  4. 主线流程剖析
  5. 关键类关系图
  6. 扩展点与常见问题
    • [6.1 扩展点(SPI 一览)](#6.1 扩展点(SPI 一览))
    • [6.2 常见问题 FAQ](#6.2 常见问题 FAQ)
    • [6.3 参考资料](#6.3 参考资料)

1. 核心理论:网关是什么

Spring Cloud Gateway 是 Spring 官方推出的微服务 API 网关 ,基于 Spring WebFlux(Project Reactor + Netty) 构建,采用非阻塞响应式 模型,是 Spring Cloud 生态中替代 Zuul 的下一代网关,也是当前微服务架构中东西向流量的事实标准入口。

1.1 从痛点到定位

微服务痛点 Gateway 的解法 对应能力
前端/外部调用散落在几十个服务地址 统一入口,按路由规则转发到目标服务 路由转发
每个服务都要自己写鉴权、日志、跨域 网关层统一处理,服务端专注业务 过滤器链
服务地址变化影响调用方 路由目标用服务名(lb://)+ 负载均衡 服务发现集成
恶意流量打垮单点服务 网关层限流、熔断、降级 流量防护
上线新版本需要灰度 按 Header/权重等断言分流 灰度发布
传统网关(Zuul1)高并发下线程堆积 Netty 事件循环 + 非阻塞转发,少量线程扛大量连接 高性能

一句话概括 :Gateway = 统一入口 + 路由转发 + 过滤器链,把横切关注点(鉴权、限流、日志、灰度)从每个微服务收口到网关一个地方。

1.2 三大核心概念:Route / Predicate / Filter

#mermaid-svg-HX9Vu4IdXLpFwcC1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HX9Vu4IdXLpFwcC1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .error-icon{fill:#552222;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .marker.cross{stroke:#333333;}#mermaid-svg-HX9Vu4IdXLpFwcC1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HX9Vu4IdXLpFwcC1 p{margin:0;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .cluster-label text{fill:#333;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .cluster-label span{color:#333;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .cluster-label span p{background-color:transparent;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .label text,#mermaid-svg-HX9Vu4IdXLpFwcC1 span{fill:#333;color:#333;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .node rect,#mermaid-svg-HX9Vu4IdXLpFwcC1 .node circle,#mermaid-svg-HX9Vu4IdXLpFwcC1 .node ellipse,#mermaid-svg-HX9Vu4IdXLpFwcC1 .node polygon,#mermaid-svg-HX9Vu4IdXLpFwcC1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .rough-node .label text,#mermaid-svg-HX9Vu4IdXLpFwcC1 .node .label text,#mermaid-svg-HX9Vu4IdXLpFwcC1 .image-shape .label,#mermaid-svg-HX9Vu4IdXLpFwcC1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .rough-node .label,#mermaid-svg-HX9Vu4IdXLpFwcC1 .node .label,#mermaid-svg-HX9Vu4IdXLpFwcC1 .image-shape .label,#mermaid-svg-HX9Vu4IdXLpFwcC1 .icon-shape .label{text-align:center;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .node.clickable{cursor:pointer;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .arrowheadPath{fill:#333333;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HX9Vu4IdXLpFwcC1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HX9Vu4IdXLpFwcC1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HX9Vu4IdXLpFwcC1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .cluster text{fill:#333;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .cluster span{color:#333;}#mermaid-svg-HX9Vu4IdXLpFwcC1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HX9Vu4IdXLpFwcC1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .icon-shape,#mermaid-svg-HX9Vu4IdXLpFwcC1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .icon-shape p,#mermaid-svg-HX9Vu4IdXLpFwcC1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .icon-shape .label rect,#mermaid-svg-HX9Vu4IdXLpFwcC1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HX9Vu4IdXLpFwcC1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HX9Vu4IdXLpFwcC1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HX9Vu4IdXLpFwcC1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 匹配成功
请求
RoutePredicateHandlerMapping

遍历路由,用 Predicate 匹配
Route 路由

id + uri + predicates + filters
过滤器链

前置 filters → 转发 → 后置 filters
目标服务

lb://service / http://ip:port

概念 作用 示例
Route(路由) 网关的基础构件,由 ID、目标 URI、一组断言和一组过滤器组成 id: user-route, uri: lb://user-service
Predicate(断言) 匹配规则(Java 8 Predicate<ServerWebExchange>),全部匹配才走该路由 Path=/user/**Header=X-Token
Filter(过滤器) 对请求/响应做处理,分全局过滤器 (GlobalFilter)与路由过滤器(GatewayFilter) StripPrefix=1AddRequestHeader

关键认知 :理解 Gateway 只需要记住一句话------请求进来 → 按 Predicate 找到 Route → 依次过 Filter 链 → 转发到 Route 指定的 URI。所有配置(yml/DSL)最终都转化为这三个对象。

1.3 与 Zuul / Nginx 对比选型

维度 Spring Cloud Gateway Zuul 1.x Zuul 2.x Nginx
技术底座 WebFlux + Reactor + Netty Servlet(阻塞) Netty(异步) C + epoll
线程模型 非阻塞事件循环 每请求一线程(线程池) 事件驱动 事件驱动
动态路由 ✅ 支持(配置中心+事件刷新) ❌ 需重启 ⚠️ 有限 需 reload/openresty 脚本
编程扩展 ✅ GlobalFilter/断言工厂,Java 生态 Filter 有限 Filter Lua(OpenResty)
与微服务生态集成 ⭐ Spring Cloud 原生(Nacos/Eureka 服务发现) Ribbon 时代产物 一般 需自建服务发现
性能(纯转发) 高(略低于 Nginx) ⭐ 最高
维护状态 ⭐ 官方活跃 停止维护 停止维护 活跃

关键认知南北向流量 (公网入口)通常用 Nginx/K8s Ingress,东西向流量(微服务间/内部 API)用 Gateway------两者可叠加(Nginx 在最外层做负载与 SSL,Gateway 做业务路由与治理)。若团队强 Java 技术栈且需要动态路由、灰度、统一鉴权,则 Gateway 一站式。

1.4 版本体系与兼容矩阵

Spring Cloud 发布列车 Spring Boot Gateway 版本 说明
2025.1(Oakwood) 4.0.x / 4.1.x 5.0.x(最新稳定 5.0.1) 基于 Spring Framework 7,当前主力线
2025.0(Northfields) 3.5.x 4.3.x 稳定线
2024.0(Moorgate) 3.4.x 4.2.x 稳定线
2023.0(Leyton) 3.2.x / 3.3.x 4.1.x 存量较多

⚠️ 版本强校验:Spring Cloud 内置兼容性验证器,Boot 与 Cloud 版本不匹配会直接拒绝启动 (报 Spring Boot [x.x.x] is not compatible with this Spring Cloud release train)。升级顺序建议:JDK → Spring Boot → Spring Cloud → Spring Cloud Alibaba


2. 设计思想与核心机制 ★★★

2.1 基于 WebFlux 的响应式架构

这是 Gateway 与 Zuul 1.x 的本质区别

维度 Gateway(WebFlux) Zuul 1.x(Servlet)
服务器 Netty(NIO 事件循环) Tomcat(BIO,每请求一线程)
线程使用 少量 EventLoop 线程处理海量连接 200 线程池 → 200 并发上限
阻塞点 全链路非阻塞(转发用 Netty Client) 请求转发阻塞线程
高并发表现 线程不随连接数增长 线程堆积、OOM 风险
  • 底层基于 Spring WebFlux + Reactor :请求以 ServerWebExchange 封装,链路全程 Mono/Flux 响应式流;
  • 转发由 Reactor Netty 的 HttpClient 完成,请求进来 → 转发出去 → 响应写回,全程无阻塞 I/O
  • ⚠️ 约束 :不能与 spring-boot-starter-web(Spring MVC)共存,否则启动报 Spring MVC found on classpath;引入 Redis 限流时也要用响应式客户端(spring-boot-starter-data-redis-reactive)。

2.2 请求处理链路总览

#mermaid-svg-2IgfNtoPrUGWDxH2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2IgfNtoPrUGWDxH2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2IgfNtoPrUGWDxH2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2IgfNtoPrUGWDxH2 .error-icon{fill:#552222;}#mermaid-svg-2IgfNtoPrUGWDxH2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2IgfNtoPrUGWDxH2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2IgfNtoPrUGWDxH2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2IgfNtoPrUGWDxH2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2IgfNtoPrUGWDxH2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2IgfNtoPrUGWDxH2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2IgfNtoPrUGWDxH2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2IgfNtoPrUGWDxH2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2IgfNtoPrUGWDxH2 .marker.cross{stroke:#333333;}#mermaid-svg-2IgfNtoPrUGWDxH2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2IgfNtoPrUGWDxH2 p{margin:0;}#mermaid-svg-2IgfNtoPrUGWDxH2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2IgfNtoPrUGWDxH2 .cluster-label text{fill:#333;}#mermaid-svg-2IgfNtoPrUGWDxH2 .cluster-label span{color:#333;}#mermaid-svg-2IgfNtoPrUGWDxH2 .cluster-label span p{background-color:transparent;}#mermaid-svg-2IgfNtoPrUGWDxH2 .label text,#mermaid-svg-2IgfNtoPrUGWDxH2 span{fill:#333;color:#333;}#mermaid-svg-2IgfNtoPrUGWDxH2 .node rect,#mermaid-svg-2IgfNtoPrUGWDxH2 .node circle,#mermaid-svg-2IgfNtoPrUGWDxH2 .node ellipse,#mermaid-svg-2IgfNtoPrUGWDxH2 .node polygon,#mermaid-svg-2IgfNtoPrUGWDxH2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2IgfNtoPrUGWDxH2 .rough-node .label text,#mermaid-svg-2IgfNtoPrUGWDxH2 .node .label text,#mermaid-svg-2IgfNtoPrUGWDxH2 .image-shape .label,#mermaid-svg-2IgfNtoPrUGWDxH2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-2IgfNtoPrUGWDxH2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2IgfNtoPrUGWDxH2 .rough-node .label,#mermaid-svg-2IgfNtoPrUGWDxH2 .node .label,#mermaid-svg-2IgfNtoPrUGWDxH2 .image-shape .label,#mermaid-svg-2IgfNtoPrUGWDxH2 .icon-shape .label{text-align:center;}#mermaid-svg-2IgfNtoPrUGWDxH2 .node.clickable{cursor:pointer;}#mermaid-svg-2IgfNtoPrUGWDxH2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2IgfNtoPrUGWDxH2 .arrowheadPath{fill:#333333;}#mermaid-svg-2IgfNtoPrUGWDxH2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2IgfNtoPrUGWDxH2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2IgfNtoPrUGWDxH2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2IgfNtoPrUGWDxH2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2IgfNtoPrUGWDxH2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2IgfNtoPrUGWDxH2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2IgfNtoPrUGWDxH2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2IgfNtoPrUGWDxH2 .cluster text{fill:#333;}#mermaid-svg-2IgfNtoPrUGWDxH2 .cluster span{color:#333;}#mermaid-svg-2IgfNtoPrUGWDxH2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2IgfNtoPrUGWDxH2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2IgfNtoPrUGWDxH2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-2IgfNtoPrUGWDxH2 .icon-shape,#mermaid-svg-2IgfNtoPrUGWDxH2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2IgfNtoPrUGWDxH2 .icon-shape p,#mermaid-svg-2IgfNtoPrUGWDxH2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2IgfNtoPrUGWDxH2 .icon-shape .label rect,#mermaid-svg-2IgfNtoPrUGWDxH2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2IgfNtoPrUGWDxH2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2IgfNtoPrUGWDxH2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2IgfNtoPrUGWDxH2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 响应逆序经过

后置 post filter
写回响应
客户端请求

HTTP
HttpWebHandlerAdapter

(WebFlux 入口)
DispatcherHandler

(分派)
RoutePredicateHandlerMapping

(路由定位)
FilteringWebHandler

(组装全局过滤器链)
GlobalFilter 链

(按 Order 排序执行)
NettyRoutingFilter

(Reactor Netty 转发)
目标服务

2.3 路由定位机制

  • 核心类 RoutePredicateHandlerMapping :启动时从 RouteLocator 拿到全部路由,请求到达后按路由定义顺序遍历 ,依次用路由的 Predicate 组合(and 逻辑)匹配 ServerWebExchange第一个全部匹配的路由生效
  • 匹配失败返回 404;所以路由顺序很重要(更具体的路由放前面);
  • 路由来源(RouteLocator 实现):
    • RouteDefinitionRouteLocator:读 yml 配置/GatewayProperties 的路由定义;
    • DiscoveryClientRouteLocator:结合服务发现(Nacos/Eureka),lb:// 自动展开为实例地址;
    • 多个 RouteLocatorCompositeRouteLocator 合并,外层套 CachingRouteLocator 缓存。

2.4 过滤器链执行机制

  • GatewayFilter(路由级) :只作用于其所属路由,由 GatewayFilterAdapter 包装成 GlobalFilter 后统一执行;
  • GlobalFilter(全局级) :作用于所有请求,按 Ordered 接口的 order 值从小到大排列执行;
  • 前置与后置 :每个过滤器在 chain.filter(exchange) 之前 的逻辑是前置(pre),之后 的是后置(post);响应返回时后置逻辑按逆序执行(类似责任链 + 栈);
  • 核心转发过滤器:NettyRoutingFilter(http/https 转发)、ForwardRoutingFilterforward: 内部转发)等。

关键认知 :Filter 链 = 排序好的责任链。前置逻辑顺序执行、后置逻辑逆序执行,理解这一点才能正确写日志、计时、改写响应的自定义过滤器。

2.5 内置断言工厂与过滤器工厂

常用断言工厂(Predicate Factory)

断言 作用 示例
Path 按路径匹配(Ant 风格) Path=/user/**
Query 按查询参数匹配 Query=version, v\d+
Method 按 HTTP 方法匹配 Method=GET,POST
Header 按请求头匹配 Header=X-Request-Id, \d+
Cookie 按 Cookie 匹配 Cookie=sessionId, .+
Host 按 Host 匹配 Host=**.example.com
RemoteAddr 按来源 IP 段匹配 RemoteAddr=192.168.1.0/24
After/Before/Between 按时间窗口匹配(定时发布) After=2026-08-25T00:00:00+08:00[Asia/Shanghai]
Weight 按权重分流(灰度,配合 group) Weight=groupA, 80

常用过滤器工厂(GatewayFilter Factory)

过滤器 作用
StripPrefix=1 转发前剥掉 1 段路径(/user/a/a
PrefixPath=/api 转发前加前缀
SetPath / RewritePath 重写路径(RewritePath 支持正则捕获组)
AddRequestHeader / AddRequestParameter 加请求头/参数(如透传用户信息)
AddResponseHeader / RemoveRequestHeader 改响应头/删请求头
RequestSize 限制请求体大小
Retry 转发失败重试
RequestRateLimiter 限流(需 Redis)
CircuitBreaker 熔断(需 Resilience4j)

2.6 负载均衡与动态路由

  • uri: lb://service-name 触发负载均衡:由 spring-cloud-starter-loadbalancerReactiveLoadBalancer 从服务发现中心取实例并选择(轮询等策略);
  • ⚠️ 自 Spring Cloud 2020 起 Ribbon 已移除 ,必须用 loadbalancer starter,否则 lb:// 不可用;
  • 动态路由 原理:修改路由配置后发布 RefreshRoutesEventCachingRouteLocator 监听事件清空缓存重新构建路由,无需重启
  • 实践中常配合 Nacos:Gateway 接入 Nacos 配置中心,路由 yml 放配置中心,改动后触发刷新(详见 3.4)。

2.7 限流机制:Redis 令牌桶

  • RequestRateLimiter 过滤器基于 Redis + Lua 脚本实现令牌桶算法(原子操作);
  • 参数:redis-rate-limiter.replenishRate(每秒补充令牌数)、redis-rate-limiter.burstCapacity(桶容量,允许的突发流量)、requestedTokens(每次消耗令牌数);
  • 限流维度KeyResolver Bean 决定:可按 IP、用户 ID、接口路径等取 key,每个 key 独立一个桶;
  • 超限默认返回 HTTP 429 ,可通过 denyEmptyKey/自定义处理器定制返回。

2.8 熔断降级集成(Resilience4j)

  • CircuitBreaker 过滤器 + spring-cloud-starter-circuitbreaker-reactor-resilience4j:下游超时/异常率过高时熔断,配合 fallbackUri: forward:/fallback 走降级接口;
  • 断路器状态(CLOSED/OPEN/HALF_OPEN)由 Resilience4j 管理,熔断期间直接短路返回降级,保护下游。

2.9 Gateway 5.0 新特性

特性 说明
异步响应支持 支持异步响应处理(如 SSE/流式场景的增强)
WebSocket 路由 原生 WebSocket 路由与转发支持
Proxy Exchange 变体 除完整网关 Server 变体外,提供轻量 Proxy Exchange,可直接用于基于注解的 WebFlux/MVC 应用做代理
基于 Spring Framework 7 与 Spring Boot 4.0 时代对齐

3. 使用实战 ★★★

3.1 快速接入:第一个路由

xml 复制代码
<!-- pom.xml(Boot 3.4+/4.0+ 示例,版本随 1.4 兼容矩阵选择) -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 服务发现 + 负载均衡(lb:// 必需) -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
yaml 复制代码
# application.yml
server:
  port: 8080                      # 网关默认 8080
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
        - id: user-service        # 路由唯一 ID
          uri: lb://user-service  # lb:// 服务名,走负载均衡
          predicates:
            - Path=/user/**       # 匹配 /user/ 开头的请求
          filters:
            - StripPrefix=1       # 转发前去掉第一段 /user

启动后访问 http://localhost:8080/user/list → 转发到 user-service/list

3.2 配置式路由详解

yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        # 1) 展开写法:多个断言是 AND 关系
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
            - Method=GET,POST
            - Header=X-Tenant, \d+          # 带合法租户头才放行
          filters:
            - AddRequestHeader=X-Gateway, api-gateway   # 透传网关标识
            - Retry=3                        # 失败重试 3 次
        # 2) 短路写法:逗号分隔同一断言的多值
        - id: demo-route
          uri: http://httpbin.org:80
          predicates:
            - Path=/api/**,/demo/**          # 短路:两个路径都匹配
          filters:
            - PrefixPath=/anything
        # 3) 限流(Redis 令牌桶)
        - id: rate-limit-route
          uri: lb://demo-service
          predicates:
            - Path=/api/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10   # 每秒补充 10 个令牌
                redis-rate-limiter.burstCapacity: 20   # 桶容量 20(允许突发)
                key-resolver: "#{@ipKeyResolver}"      # 按 IP 限流
      globalcors:                     # 全局跨域(收口到网关)
        add-to-simple-url-handler-mapping: true
        cors-configurations:
          '[/**]':
            allowedOrigins: "https://example.com"
            allowedMethods: "*"
            allowedHeaders: "*"

3.3 Java DSL 编程式路由

java 复制代码
@Configuration
public class GatewayRoutes {

    @Bean
    public RouteLocator customRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
                // 编程式路由:按 Host + Path 匹配
                .route("user-route", r -> r
                        .host("*.user.example.com")
                        .and().path("/user/**")
                        .filters(f -> f.stripPrefix(1)
                                       .addRequestHeader("X-Gateway", "dsl"))
                        .uri("lb://user-service"))
                // 按时间窗口发布
                .route("activity-route", r -> r
                        .after(ZonedDateTime.parse("2026-09-01T00:00:00+08:00[Asia/Shanghai]"))
                        .uri("lb://activity-service"))
                .build();
    }
}

3.4 动态路由(Nacos 配置中心)

xml 复制代码
<!-- 接入 Nacos 配置中心,路由 yml 上云 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
yaml 复制代码
# bootstrap.yml(或 spring.config.import 新写法)
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        shared-configs:
          - data-id: gateway-routes.yaml   # 路由配置单独放一个 dataId
            group: DEFAULT_GROUP
            refresh: true                  # ⭐ 开启刷新
java 复制代码
// 监听配置变更,重建路由(RefreshRoutesEvent 驱动)
@Component
public class DynamicRouteListener {

    @EventListener(RefreshRoutesEvent.class)
    public void onRefresh(RefreshRoutesEvent event) {
        // CachingRouteLocator 收到事件后自动重建路由,这里可追加自定义逻辑(如打日志)
        log.info("routes refreshed: {}", event.getSource());
    }
}

关键认知 :改 Nacos 里的 gateway-routes.yaml → 网关收到配置刷新 → 发布 RefreshRoutesEvent → 路由热生效,全程零重启。这是 Gateway 区别于 Nginx 静态配置的核心竞争力。

3.5 自定义全局过滤器:统一鉴权

java 复制代码
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String token = request.getHeaders().getFirst("X-Token");
        // 前置逻辑:鉴权失败直接短路,不进入后续链
        if (token == null || !token.startsWith("Bearer ")) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();   // 不调用 chain.filter,请求终止
        }
        // 通过:把用户信息透传给下游服务
        ServerHttpRequest mutated = request.mutate()
                .header("X-User-Id", parseUserId(token))
                .build();
        return chain.filter(exchange.mutate().request(mutated).build());
    }

    @Override
    public int getOrder() {
        return -100;   // 越小越先执行,保证鉴权在最前
    }
}

3.6 限流实战(Redis RateLimiter)

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>  <!-- ⚠️ 必须响应式客户端 -->
</dependency>
java 复制代码
@Configuration
public class RateLimitConfig {

    // 按客户端 IP 限流(也可按 userId/接口维度)
    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(
                Objects.requireNonNull(exchange.getRequest().getRemoteAddress())
                        .getAddress().getHostAddress());
    }
}

⚠️ 踩坑提示:key-resolver: "#{@ipKeyResolver}" 中的 Bean 名必须与 @Bean 方法名一致;限流不生效先查 Redis 连接与 Bean 命名。超限返回 429,可自定义 RateLimiter 响应处理器返回业务友好提示。

3.7 熔断与重试配置

xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
yaml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: payment-service
          uri: lb://payment-service
          predicates:
            - Path=/pay/**
          filters:
            - name: CircuitBreaker
              args:
                name: paymentCB                  # 断路器实例名
                fallbackUri: forward:/fallback   # 熔断后转内部降级接口
java 复制代码
// 网关自身提供降级接口
@RestController
public class FallbackController {
    @GetMapping("/fallback")
    public Mono<String> fallback() {
        return Mono.just("{\"code\": 503, \"msg\": \"服务暂时不可用\"}");
    }
}

4. 主线流程剖析 ★★★

目标服务 Nacos 服务发现 Gateway(Netty) 客户端 目标服务 Nacos 服务发现 Gateway(Netty) 客户端 #mermaid-svg-cYSoen9Yy8FSjEtM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cYSoen9Yy8FSjEtM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cYSoen9Yy8FSjEtM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cYSoen9Yy8FSjEtM .error-icon{fill:#552222;}#mermaid-svg-cYSoen9Yy8FSjEtM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cYSoen9Yy8FSjEtM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cYSoen9Yy8FSjEtM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cYSoen9Yy8FSjEtM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cYSoen9Yy8FSjEtM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cYSoen9Yy8FSjEtM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cYSoen9Yy8FSjEtM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cYSoen9Yy8FSjEtM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cYSoen9Yy8FSjEtM .marker.cross{stroke:#333333;}#mermaid-svg-cYSoen9Yy8FSjEtM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cYSoen9Yy8FSjEtM p{margin:0;}#mermaid-svg-cYSoen9Yy8FSjEtM .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-cYSoen9Yy8FSjEtM text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-cYSoen9Yy8FSjEtM .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-cYSoen9Yy8FSjEtM .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-cYSoen9Yy8FSjEtM .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-cYSoen9Yy8FSjEtM .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-cYSoen9Yy8FSjEtM #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-cYSoen9Yy8FSjEtM .sequenceNumber{fill:white;}#mermaid-svg-cYSoen9Yy8FSjEtM #sequencenumber{fill:#333;}#mermaid-svg-cYSoen9Yy8FSjEtM #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-cYSoen9Yy8FSjEtM .messageText{fill:#333;stroke:none;}#mermaid-svg-cYSoen9Yy8FSjEtM .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-cYSoen9Yy8FSjEtM .labelText,#mermaid-svg-cYSoen9Yy8FSjEtM .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-cYSoen9Yy8FSjEtM .loopText,#mermaid-svg-cYSoen9Yy8FSjEtM .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-cYSoen9Yy8FSjEtM .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-cYSoen9Yy8FSjEtM .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-cYSoen9Yy8FSjEtM .noteText,#mermaid-svg-cYSoen9Yy8FSjEtM .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-cYSoen9Yy8FSjEtM .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-cYSoen9Yy8FSjEtM .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-cYSoen9Yy8FSjEtM .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-cYSoen9Yy8FSjEtM .actorPopupMenu{position:absolute;}#mermaid-svg-cYSoen9Yy8FSjEtM .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-cYSoen9Yy8FSjEtM .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-cYSoen9Yy8FSjEtM .actor-man circle,#mermaid-svg-cYSoen9Yy8FSjEtM line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-cYSoen9Yy8FSjEtM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} GET /user/list WebFlux 入口 → DispatcherHandler RoutePredicateHandlerMapping 按序匹配 Predicate(Path=/user/** 命中) FilteringWebHandler 组装过滤器链 (Order 排序 + 路由级过滤器包装) 前置过滤器依次执行 (鉴权/限流/改请求头...) lb:// 查询 user-service 实例列表 返回可用实例(轮询选一个) NettyRoutingFilter 异步转发 /list 200 响应 后置过滤器逆序执行 (改响应头/日志/计时...) 写回响应

关键认知 :Gateway 的请求生命周期 = 匹配路由(同步快速)→ 前置过滤链 → 异步非阻塞转发 → 后置过滤链(逆序)→ 写回。全程无阻塞 I/O,线程不随请求数线性增长。


5. 关键类关系图

#mermaid-svg-0cbAXWwJhQd6kwcV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0cbAXWwJhQd6kwcV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0cbAXWwJhQd6kwcV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0cbAXWwJhQd6kwcV .error-icon{fill:#552222;}#mermaid-svg-0cbAXWwJhQd6kwcV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0cbAXWwJhQd6kwcV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0cbAXWwJhQd6kwcV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0cbAXWwJhQd6kwcV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0cbAXWwJhQd6kwcV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0cbAXWwJhQd6kwcV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0cbAXWwJhQd6kwcV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0cbAXWwJhQd6kwcV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0cbAXWwJhQd6kwcV .marker.cross{stroke:#333333;}#mermaid-svg-0cbAXWwJhQd6kwcV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0cbAXWwJhQd6kwcV p{margin:0;}#mermaid-svg-0cbAXWwJhQd6kwcV g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-0cbAXWwJhQd6kwcV g.classGroup text .title{font-weight:bolder;}#mermaid-svg-0cbAXWwJhQd6kwcV .cluster-label text{fill:#333;}#mermaid-svg-0cbAXWwJhQd6kwcV .cluster-label span{color:#333;}#mermaid-svg-0cbAXWwJhQd6kwcV .cluster-label span p{background-color:transparent;}#mermaid-svg-0cbAXWwJhQd6kwcV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0cbAXWwJhQd6kwcV .cluster text{fill:#333;}#mermaid-svg-0cbAXWwJhQd6kwcV .cluster span{color:#333;}#mermaid-svg-0cbAXWwJhQd6kwcV .nodeLabel,#mermaid-svg-0cbAXWwJhQd6kwcV .edgeLabel{color:#131300;}#mermaid-svg-0cbAXWwJhQd6kwcV .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-0cbAXWwJhQd6kwcV .label text{fill:#131300;}#mermaid-svg-0cbAXWwJhQd6kwcV .labelBkg{background:#ECECFF;}#mermaid-svg-0cbAXWwJhQd6kwcV .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-0cbAXWwJhQd6kwcV .classTitle{font-weight:bolder;}#mermaid-svg-0cbAXWwJhQd6kwcV .node rect,#mermaid-svg-0cbAXWwJhQd6kwcV .node circle,#mermaid-svg-0cbAXWwJhQd6kwcV .node ellipse,#mermaid-svg-0cbAXWwJhQd6kwcV .node polygon,#mermaid-svg-0cbAXWwJhQd6kwcV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0cbAXWwJhQd6kwcV .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-0cbAXWwJhQd6kwcV g.clickable{cursor:pointer;}#mermaid-svg-0cbAXWwJhQd6kwcV g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-0cbAXWwJhQd6kwcV g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-0cbAXWwJhQd6kwcV .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-0cbAXWwJhQd6kwcV .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-0cbAXWwJhQd6kwcV .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-0cbAXWwJhQd6kwcV .dashed-line{stroke-dasharray:3;}#mermaid-svg-0cbAXWwJhQd6kwcV .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-0cbAXWwJhQd6kwcV #compositionStart,#mermaid-svg-0cbAXWwJhQd6kwcV .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-0cbAXWwJhQd6kwcV #compositionEnd,#mermaid-svg-0cbAXWwJhQd6kwcV .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-0cbAXWwJhQd6kwcV #dependencyStart,#mermaid-svg-0cbAXWwJhQd6kwcV .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-0cbAXWwJhQd6kwcV #dependencyStart,#mermaid-svg-0cbAXWwJhQd6kwcV .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-0cbAXWwJhQd6kwcV #extensionStart,#mermaid-svg-0cbAXWwJhQd6kwcV .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-0cbAXWwJhQd6kwcV #extensionEnd,#mermaid-svg-0cbAXWwJhQd6kwcV .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-0cbAXWwJhQd6kwcV #aggregationStart,#mermaid-svg-0cbAXWwJhQd6kwcV .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-0cbAXWwJhQd6kwcV #aggregationEnd,#mermaid-svg-0cbAXWwJhQd6kwcV .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-0cbAXWwJhQd6kwcV #lollipopStart,#mermaid-svg-0cbAXWwJhQd6kwcV .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-0cbAXWwJhQd6kwcV #lollipopEnd,#mermaid-svg-0cbAXWwJhQd6kwcV .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-0cbAXWwJhQd6kwcV .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-0cbAXWwJhQd6kwcV .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0cbAXWwJhQd6kwcV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0cbAXWwJhQd6kwcV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0cbAXWwJhQd6kwcV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 获取路由
yml 配置路由
服务发现路由
命中后委托执行
链式执行
路由级过滤适配
<<interface>>
RouteLocator
+getRoutes() : Flux<Route>
CachingRouteLocator
CompositeRouteLocator
RouteDefinitionRouteLocator
DiscoveryClientRouteLocator
RoutePredicateHandlerMapping
+getHandler()
FilteringWebHandler
+handle()
<<interface>>
GlobalFilter
+filter()
NettyRoutingFilter
+getOrder()
ForwardRoutingFilter
GatewayFilterAdapter
+filter() : 将路由级过滤包装为全局过滤
<<interface>>
GatewayFilterFactory
+apply()
StripPrefixGatewayFilterFactory
<<interface>>
RoutePredicateFactory
+apply()
PathRoutePredicateFactory


6. 扩展点与常见问题

6.1 扩展点(SPI 一览)

扩展点 用途 示例
GlobalFilter 全局前置/后置处理 统一鉴权、日志、灰度头透传
GatewayFilterFactory 自定义路由级过滤器(yml 可引用) 自定义加解密、签名校验
RoutePredicateFactory 自定义断言规则 按业务字段路由
RouteLocator 自定义路由来源 从数据库/配置中心加载路由
RefreshRoutesEvent 动态路由刷新 配合 Nacos/Apollo 热更新
KeyResolver 限流维度自定义 按用户/租户/IP 限流
自定义异常处理器 网关统一错误响应 ErrorWebExceptionHandler 定制返回体

6.2 常见问题 FAQ

Q1:启动报错 Spring MVC found on classpath

spring-cloud-starter-gateway 基于 WebFlux,与 spring-boot-starter-web(Spring MVC)互斥。检查依赖树排除 web starter,或确认项目确实要用 MVC 时改走 Gateway 的 Proxy Exchange 变体(5.0+,可用于 MVC 应用做代理)。

Q2:路由配置了但不生效 / 404?

按顺序排查:① 断言是否全匹配(多个 Predicate 是 AND 关系);② 路由顺序------Gateway 按定义顺序取第一个匹配 的路由,更具体的路由要放前面;③ Path 大小写与路径段数是否与 StripPrefix 搭配正确;④ 短路写法(逗号分隔)与展开写法(列表)混用导致语法错误。

Q3:转发 502 / 503 连不上目标服务?

lb:// 需要 loadbalancer starter + 服务发现组件(Nacos/Eureka)缺一不可 ;检查目标服务是否已注册、实例是否健康(可先换 http://ip:port 直连验证网络);超时问题检查 connect-timeout/response-timeout 配置。

Q4:高并发下网关线程数暴涨 / 响应慢?

排查是否误引了阻塞组件(JDBC 驱动、阻塞 Redis 客户端、Thread.sleep 类自定义过滤器)------WebFlux 链路中任何阻塞调用 都会拖垮 Netty 事件循环;Redis 限流必须用 -data-redis-reactive

Q5:怎么做到路由不重启动态生效?

路由配置放 Nacos(shared-configsrefresh: true)→ 配置变更触发 RefreshRoutesEvent → 网关重建路由。如需更完整的路由管理(增删改查+审计),可在 RouteLocator 上自研 DB 版路由源(见 6.1)。

Q6:限流配置了但没生效?

key-resolver 引用的 Bean 名与 @Bean 方法名不一致;② 未用 spring-boot-starter-data-redis-reactive(阻塞客户端在 WebFlux 下异常);③ Redis 连不上(限流静默失败,看日志);④ 理解参数语义:replenishRate=10每秒补充 10 令牌burstCapacity 是桶容量,实际 TPS 上限受两者共同影响。

Q7:Gateway 和 Nginx 如何分工?

常见架构:公网 → Nginx(SSL 卸载、四层/七层负载、静态资源)→ Gateway(业务路由、鉴权、限流、灰度)→ 微服务。中小规模也可只保留 Gateway(配合 K8s Ingress),取决于团队对 Lua vs Java 的掌控力。

Q8:升级 Spring Boot 4 时 Gateway 要注意什么?

必须同时升级 Spring Cloud 到 2025.1(Oakwood)Gateway 到 5.0.x(基于 Framework 7),否则兼容性验证器阻止启动;留意 5.0 的破坏性变更(旧 API 清理),以及 Proxy Exchange / WebSocket 路由等新能力是否适用你的场景。

6.3 参考资料

相关推荐
国科安芯17 分钟前
小卫星综合电子系统中功能安全与抗辐射加固的协同设计研究
嵌入式硬件·安全·架构·risc-v·抗辐射·小卫星·综合电子系统
catino23 分钟前
spring-事务@Transactional
java·数据库·spring
某林21231 分钟前
机器人收不住、转不动?执行器死区的原理与三层补偿设计
前端·网络·c++·架构·机器人
万年咸鱼35 分钟前
Java List 接口详解:从基础到实战
java·windows·list
深念Y36 分钟前
视频平台架构决策:从存储到转码的选型逻辑
架构·音视频
Java的搬运工38 分钟前
Spring boot 事件监听
java·spring boot·spring·编程语言
tryxr42 分钟前
Chat2Excel 项目网关服务开发
java·gateway·网关模块开发
谢亮_vipxieliang42 分钟前
ValidX vs Apache Commons Validator:功能与性能对比
java·服务器·spring boot·后端·spring cloud·apache·hibernate
tryxr1 小时前
Chat2Excel 项目通用服务开发
java·开发语言·excel·java项目开发