SpringCloudAlibaba_Gateway详解

O泡李华 10

Spring Cloud Alibaba:Gateway 详解

课程位置:第四阶段「微服务项目 + AI 应用」第 4 课
前置知识:微服务体系架构、Nacos、Feign、LoadBalancer、Sentinel、Spring Boot 3
本课主题:Spring Cloud Gateway
后续课程:若依 Cloud 框架部署与代码修改器使用
学习目标:从“前端直接访问多个微服务”的问题开始,理解 API Gateway 的作用,掌握 Gateway Server WebFlux、Route、Predicate、GatewayFilter、GlobalFilter、lb://、Nacos 服务发现、路径改写、CORS、统一鉴权、JWT、白名单、Filter 顺序、超时、限流、Actuator 路由查看以及 Gateway + Sentinel 的基本配合。


一、先回顾我们现在的微服务结构

前面我们已经有:

user-service
order-service

并且:

order-service
↓ Feign
user-service

服务注册到:

Nacos

Sentinel:

保护服务内部资源和远程调用

现在还有一个问题:

前端到底访问谁?

二、如果没有 Gateway

Vue 前端可能需要记:

http://localhost:8081/users/**

http://localhost:8082/orders/**

http://localhost:8083/travel/**

http://localhost:8084/search/**

http://localhost:8085/ai/**

这会越来越乱。


三、前端直接访问微服务有什么问题

第一:

前端必须知道每个服务地址

第二:

服务端口变化
前端也可能跟着改

第三:

每个服务都要重复处理跨域

第四:

每个服务都可能重复做 Token 校验

第五:

内部服务结构直接暴露给客户端

第六:

统一限流、日志、黑名单不好做

四、于是需要 API Gateway

Gateway 可以理解为:

微服务系统统一入口

结构变成:

Vue
↓
Gateway : 8080
├─ /api/users/**  → user-service
├─ /api/orders/** → order-service
├─ /api/travel/** → travel-service
└─ /api/ai/**     → ai-service

前端只需要知道:

Gateway 地址

五、Gateway 最核心职责

可以先记:

路由

过滤

统一入口

进一步包括:

统一鉴权

跨域

日志

限流

Header 处理

灰度路由

监控

六、Gateway 不应该干什么

Gateway 不应该变成:

超级业务服务

不要把:

订单金额计算

库存扣减

审批业务

用户积分计算

放进 Gateway。


七、正确职责边界

Gateway:

处理入口横切逻辑

业务服务:

处理真正业务

八、Gateway 和 Nginx 的关系

两者都可以:

反向代理

常见部署:

Internet
↓
Nginx
↓
Spring Cloud Gateway
↓
Microservices

九、Nginx 更擅长

TLS/HTTPS

静态资源

高性能反向代理

网络入口

基础负载均衡

十、Gateway 更擅长

Spring Cloud 服务发现

业务路由

Java 过滤器

统一认证上下文

与 Spring 生态集成

十一、这一课使用哪种 Gateway

Spring Cloud Gateway 现在有:

Server WebFlux

Server WebMVC

课程选择:

Spring Cloud Gateway Server WebFlux

这是传统 Spring Cloud 微服务项目中非常常见的一条路线。


十二、为什么要特别说 WebFlux

因为 Gateway Server WebFlux:

不是 Spring MVC Servlet 模型

它建立在:

Spring WebFlux
Project Reactor
Reactor Netty

之上。


十三、最容易踩的坑

不要在 Gateway WebFlux 模块里无脑加入:

spring-boot-starter-web

然后再混:

Tomcat
Servlet Filter
HttpServletRequest

十四、Gateway Server WebFlux 的运行时

官方明确说明:

依赖 Spring WebFlux
+
Reactor Netty

并且:

不运行在传统 Servlet Container 模式中

十五、所以 Gateway 模块要有“反应式思维”

你会看到:

Mono<Void>

而不是普通 Controller 常见:

void
String
AjaxResult

十六、版本变化:Starter 名称

Spring Cloud 2025.0.0 对 Gateway 模块命名做了调整。

课程当前基线:

Spring Cloud 2025.0.x
Spring Boot 3.5.x

Gateway 属于:

4.3.x

十七、新 Starter 名称

推荐:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>
        spring-cloud-starter-gateway-server-webflux
    </artifactId>
</dependency>

十八、旧教程可能看到

spring-cloud-starter-gateway

或者:

spring-cloud-starter-gateway-server

这些属于:

旧命名

十九、2025.0.x 为什么改名

为了明确区分:

Server / Proxy Exchange

WebFlux / WebMVC

新名字更清晰:

gateway-server-webflux

二十、配置前缀也发生变化

旧教程常见:

spring:
  cloud:
    gateway:
      routes:

Spring Cloud 2025.0.x 新前缀:

spring:
  cloud:
    gateway:
      server:
        webflux:
          routes:

二十一、这一点非常重要

这节课统一使用:

spring.cloud.gateway.server.webflux.*

避免继续写旧式:

spring.cloud.gateway.*

二十二、创建 gateway 模块

父工程现在:

spring-cloud-demo
├─ user-service
├─ order-service
└─ gateway

二十三、父 pom 加 module

<modules>
    <module>user-service</module>
    <module>order-service</module>
    <module>gateway</module>
</modules>

二十四、gateway pom

<dependencies>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>
            spring-cloud-starter-gateway-server-webflux
        </artifactId>
    </dependency>

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

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>
            spring-cloud-starter-loadbalancer
        </artifactId>
    </dependency>

</dependencies>

二十五、为什么 Gateway 也需要 Nacos

Gateway 的目标:

把请求转发给 user-service

它必须知道:

user-service 当前在哪台机器

所以:

Gateway 也是服务消费者

需要:

服务发现

二十六、Gateway application.yml

server:
  port: 8080

spring:
  application:
    name: gateway

  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

二十七、启动后 Nacos

应该看到:

gateway
user-service
order-service

二十八、Route 是什么

Gateway 最核心概念:

Route

中文:

路由

二十九、一个 Route 包含什么

最重要:

id

uri

predicates

filters

三十、可以理解成

如果请求满足 predicates
↓
先经过 filters
↓
转发到 uri

三十一、第一条最简单路由

spring:
  cloud:
    gateway:
      server:
        webflux:
          routes:
            - id: user-service-route
              uri: http://localhost:8081
              predicates:
                - Path=/users/**

三十二、这个路由什么意思

请求:

GET http://localhost:8080/users/1

匹配:

Path=/users/**

Gateway 转发:

http://localhost:8081/users/1

三十三、这只是第一步

目前:

uri 仍然写死 localhost:8081

我们已经学过:

这种方式不适合微服务

三十四、改成 lb://

spring:
  cloud:
    gateway:
      server:
        webflux:
          routes:
            - id: user-service-route
              uri: lb://user-service
              predicates:
                - Path=/users/**

三十五、lb 是什么

lb
=
load balance

表示:

按照服务名
通过 LoadBalancer 找实例

三十六、完整过程

请求 /users/1
↓
Gateway 匹配 user-service-route
↓
看到 lb://user-service
↓
Nacos Discovery
↓
获取 user-service 实例
↓
ReactiveLoadBalancer
↓
选择一个实例
↓
HTTP 转发

三十七、如果 user-service 有两个实例

例如:

127.0.0.1:8081

127.0.0.1:8083

Gateway:

不需要写两条路由

仍然:

lb://user-service

LoadBalancer:

选择实例

三十八、找不到实例会怎样

Gateway WebFlux 默认:

返回 503

因为:

Service Unavailable

三十九、这类错误怎么排查

Nacos 中有没有服务

服务名是否一致

Namespace 是否一致

实例是否健康

LoadBalancer 依赖是否存在

四十、order-service 路由

spring:
  cloud:
    gateway:
      server:
        webflux:
          routes:
            - id: user-service-route
              uri: lb://user-service
              predicates:
                - Path=/users/**

            - id: order-service-route
              uri: lb://order-service
              predicates:
                - Path=/orders/**

四十一、现在前端只访问 Gateway

用户:

GET localhost:8080/users/1

订单:

GET localhost:8080/orders/1001

前端:

不再关心 8081 / 8082

四十二、Predicate 是什么

Predicate:

断言

可以理解:

“什么请求应该匹配这条路由?”

四十三、最常用 Path Predicate

predicates:
  - Path=/users/**

意思:

路径满足 /users/**

才进入这条 Route。


四十四、Path 通配符

/users/**

可以匹配:

/users/1

/users/list

/users/profile/avatar

四十五、Method Predicate

predicates:
  - Path=/users/**
  - Method=GET

同时要求:

路径匹配
AND
请求方法是 GET

四十六、多个 Predicate 默认关系

同一个 Route 中多个 Predicate:

必须全部满足

可以理解:

AND

四十七、Header Predicate

例如:

predicates:
  - Header=X-Request-Type, internal

要求 Header:

X-Request-Type

匹配:

internal

四十八、Query Predicate

例如:

predicates:
  - Query=source, app

只有:

?source=app

满足时匹配。


四十九、Host Predicate

例如:

predicates:
  - Host=api.example.com

可以按域名:

选择路由

五十、Cookie Predicate

可以根据:

Cookie

进行匹配。


五十一、时间 Predicate

Gateway 还支持:

After

Before

Between

适合:

限时活动路由

五十二、RemoteAddr Predicate

可以根据:

来源 IP

判断。


五十三、为什么 Predicate 很强

Gateway 不只是:

URL → URL

它可以根据:

Path

Method

Header

Query

Host

IP

时间

决定:

请求去哪

五十四、不要过度设计 Predicate

普通业务项目:

Path

通常已经解决绝大多数路由。

复杂规则:

按业务需要添加

五十五、Filter 是什么

Gateway Filter:

过滤器

负责:

请求转发前后
修改请求/响应

五十六、Predicate 和 Filter 区别

Predicate
→ 要不要匹配这条路由

Filter
→ 匹配后要做什么处理

五十七、最常见 Filter:StripPrefix

假设前端统一:

/api/user/**

但是 user-service Controller 是:

/users/**

需要:

去掉一部分前缀

五十八、StripPrefix 原理

请求:

/api/users/1

配置:

filters:
  - StripPrefix=1

去掉第一个 path segment:

/api

下游变:

/users/1

五十九、完整配置

spring:
  cloud:
    gateway:
      server:
        webflux:
          routes:
            - id: user-service-route
              uri: lb://user-service
              predicates:
                - Path=/api/users/**
              filters:
                - StripPrefix=1

六十、请求过程

客户端:
/api/users/1

↓ Path 匹配

↓ StripPrefix=1

/users/1

↓ lb://user-service

user-service

六十一、StripPrefix=2

请求:

/api/v1/users/1

去掉:

/api
/v1

下游:

/users/1

六十二、不要 Strip 错了

如果 Provider 本身 Controller:

/api/users/1

你又:

StripPrefix=1

就可能:

404

六十三、Gateway 404 排查第一件事

写出:

客户端原始路径
↓
Predicate 匹配路径
↓
Filter 处理后路径
↓
Provider Controller 真正路径

四步对照。


六十四、RewritePath

如果不是简单去前缀,

可以:

重写路径

例如:

filters:
  - RewritePath=/api/users/(?<segment>.*), /users/${segment}

六十五、RewritePath 比 StripPrefix 更灵活

但也:

更容易写错正则

简单场景:

优先 StripPrefix

六十六、PrefixPath

作用相反:

给下游路径增加前缀

请求:

/users/1

可以转成:

/api/users/1

六十七、AddRequestHeader

例如:

filters:
  - AddRequestHeader=X-Gateway, cloud-demo

下游收到:

X-Gateway: cloud-demo

六十八、RemoveRequestHeader

可以删除:

某些客户端不该传给内部服务的 Header

这在:

安全设计

中很重要。


六十九、AddResponseHeader

Gateway 可以给响应加:

Header

例如:

X-Gateway-Version

七十、SetStatus

可以改变:

HTTP Status

七十一、RequestSize

可以限制:

请求体大小

例如上传入口:

防止超大请求

七十二、为什么 Gateway 很适合做请求体大小限制

因为:

越早拒绝
成本越低

不需要请求进入:

业务服务

才发现太大。


七十三、GatewayFilter 和 GlobalFilter

GatewayFilter:

只作用于某条 Route

GlobalFilter:

作用于所有匹配路由

七十四、什么时候用路由 Filter

例如:

只有文件服务
需要限制上传大小

使用:

Route Filter

七十五、什么时候用 GlobalFilter

例如:

统一日志

统一 Token 检查

TraceId

全局 Header

适合:

GlobalFilter

七十六、第一个 GlobalFilter

@Component
public class LogGlobalFilter
        implements GlobalFilter, Ordered {

    private static final Logger log =
            LoggerFactory.getLogger(
                LogGlobalFilter.class
            );

    @Override
    public Mono<Void> filter(
            ServerWebExchange exchange,
            GatewayFilterChain chain
    ) {

        String path =
                exchange
                    .getRequest()
                    .getURI()
                    .getPath();

        long start =
                System.currentTimeMillis();

        log.info(
            "Gateway request start, path={}",
            path
        );

        return chain
            .filter(exchange)
            .doFinally(signalType -> {

                long cost =
                        System.currentTimeMillis()
                        - start;

                log.info(
                    "Gateway request end, path={}, cost={}ms",
                    path,
                    cost
                );
            });
    }

    @Override
    public int getOrder() {
        return -100;
    }
}

七十七、为什么返回 Mono

因为:

Gateway WebFlux
是反应式链路

请求处理:

异步非阻塞

七十八、最重要:不要在 GlobalFilter 里阻塞

危险:

Thread.sleep(5000);

或者:

大量阻塞式数据库访问

七十九、为什么

Gateway 使用:

Netty Event Loop

如果阻塞:

少量线程被长时间占用

会严重影响:

整个 Gateway 吞吐

八十、所以 Gateway 不适合做复杂 DB 权限查询

如果每一个请求:

Gateway 查 5 张 MySQL 表

设计通常:

过重

八十一、统一认证和完整业务授权要区分

Gateway 可以做:

Token 是否存在

Token 是否有效

基础身份解析

业务服务仍应该做:

具体资源权限

数据权限

业务授权

八十二、为什么不能只靠 Gateway 授权

内部服务可能:

被其他内部服务直接调用

而且:

业务权限离业务数据更近

八十三、所以推荐分层

Gateway
→ 身份认证、基础入口安全

业务服务
→ RBAC、数据权限、业务校验

八十四、JWT 统一认证基本流程

客户端登录
↓
拿到 JWT
↓
请求 Gateway
↓
Gateway 校验 JWT
↓
解析 userId
↓
转发内部服务

八十五、白名单

例如:

/login

/captcha

/public/**

这些:

不用登录

八十六、其他路径

必须有合法 Token

八十七、白名单配置

可以:

security:
  ignore:
    paths:
      - /api/auth/login
      - /api/auth/captcha
      - /api/public/**

然后:

Gateway Filter 读取

八十八、不要硬编码几十个 if

错误:

if (
    path.equals("/login")
    || path.equals("/captcha")
    || ...
)

推荐:

配置化白名单

八十九、JWT Filter 逻辑示意

@Component
public class AuthGlobalFilter
        implements GlobalFilter, Ordered {

    private final TokenService tokenService;

    public AuthGlobalFilter(
            TokenService tokenService
    ) {
        this.tokenService =
                tokenService;
    }

    @Override
    public Mono<Void> filter(
            ServerWebExchange exchange,
            GatewayFilterChain chain
    ) {

        ServerHttpRequest request =
                exchange.getRequest();

        String path =
                request
                    .getURI()
                    .getPath();

        if (isWhiteList(path)) {
            return chain.filter(exchange);
        }

        String token =
                request
                    .getHeaders()
                    .getFirst(
                        HttpHeaders.AUTHORIZATION
                    );

        if (!tokenService.isValid(token)) {
            return unauthorized(exchange);
        }

        Long userId =
                tokenService.getUserId(token);

        ServerHttpRequest newRequest =
                request
                    .mutate()
                    .headers(headers -> {
                        headers.remove(
                            "X-User-Id"
                        );
                        headers.add(
                            "X-User-Id",
                            String.valueOf(userId)
                        );
                    })
                    .build();

        ServerWebExchange newExchange =
                exchange
                    .mutate()
                    .request(newRequest)
                    .build();

        return chain.filter(newExchange);
    }

    @Override
    public int getOrder() {
        return -200;
    }
}

九十、为什么先 remove X-User-Id

这是非常重要的安全点。

客户端可能自己发送:

X-User-Id: 1

如果 Gateway:

直接相信

就可能:

伪造身份

九十一、正确

删除客户端传来的内部身份 Header
↓
Gateway 根据合法 Token 重新生成

九十二、但下游能不能绝对相信 X-User-Id

只有在:

内部网络边界可靠
并且下游不能被公网直接访问

的情况下,

才能把 Gateway 注入 Header:

作为可信上下文的一部分

九十三、生产更严谨的方式

还可能:

内部签名

mTLS

OAuth2 Resource Server

Token Relay

Service Identity

当前课程先掌握:

不要信任客户端伪造内部 Header

九十四、Gateway 官方也支持 Spring Security

Gateway Server WebFlux 可以配合:

Spring Security

但是注意:

是 WebFlux 安全模型

九十五、WebFlux Security 用什么

常见:

SecurityWebFilterChain

ServerHttpSecurity

不是传统 Servlet:

SecurityFilterChain + HttpSecurity

那一套直接照搬。


九十六、为什么容易混

你前面 Spring Boot 普通后台:

Spring MVC

Gateway:

Spring WebFlux

两个:

Web 技术栈不同

九十七、不要在 Gateway 里使用 HttpServletRequest

WebFlux 常用:

ServerHttpRequest

ServerHttpResponse

ServerWebExchange

九十八、ServerWebExchange 可以理解成

一次 Gateway 请求上下文

里面有:

Request

Response

Attributes

九十九、Filter 顺序

GlobalFilter 可以实现:

Ordered

返回:

getOrder()

决定顺序。


一百、数字越小

通常:

优先级越高

例如:

-200

比:

-100

更早进入 pre 阶段。


一百零一、Gateway Filter 有 pre 和 post

请求:

进入

执行:

pre

转发下游。

响应回来:

post

一百零二、顺序非常有意思

如果:

Filter A order=-100

Filter B order=0

pre:

A
↓
B

post:

B
↓
A

一百零三、为什么

类似:

方法调用栈

外层先进入:

最后退出

一百零四、过滤器顺序示意

A pre
↓
B pre
↓
Route
↓
B post
↓
A post

一百零五、为什么认证 Filter 要比较早

因为未登录请求:

应该尽早拒绝

不要:

先做大量无意义工作

一百零六、但顺序不能只靠“写个 -99999”

企业项目:

所有 GlobalFilter 顺序应该统一规划

例如:

TraceId   -300
Auth      -200
Logging   -100

只是示意。


一百零七、跨域 CORS

前端:

http://localhost:5173

Gateway:

http://localhost:8080

协议/域名/端口不同:

就可能跨域

一百零八、为什么 Gateway 适合统一 CORS

因为:

所有前端请求都先经过 Gateway

可以:

集中处理

一百零九、Spring Cloud 2025.0.x CORS 配置

spring:
  cloud:
    gateway:
      server:
        webflux:
          globalcors:
            add-to-simple-url-handler-mapping: true
            cors-configurations:
              '[/**]':
                allowedOrigins:
                  - "http://localhost:5173"
                allowedMethods:
                  - GET
                  - POST
                  - PUT
                  - DELETE
                  - OPTIONS
                allowedHeaders:
                  - "*"
                allowCredentials: true
                maxAge: 3600

一百一十、add-to-simple-url-handler-mapping

这个配置对:

OPTIONS 预检请求

很有帮助。

如果预检:

没匹配某条 Route

仍然可以:

处理全局 CORS

一百一十一、为什么浏览器先发 OPTIONS

复杂跨域请求时:

浏览器先问服务器:
“我能不能这样请求?”

这就是:

Preflight

一百一十二、CORS 常见错误:Gateway 和服务都加

Gateway:

加 Access-Control-Allow-Origin

下游服务:

也加

结果可能:

响应头重复

浏览器仍然报跨域。


一百一十三、建议

如果所有外部流量统一 Gateway:

优先在 Gateway 统一处理 CORS

内部服务:

通常不需要再对浏览器开放 CORS

一百一十四、allowCredentials 与 *

如果:

allowCredentials=true

不要简单使用:

allowedOrigins: "*"

应该:

明确允许的前端 Origin

一百一十五、为什么

凭证跨域:

安全要求更严格

Spring CORS 配置也会对此进行校验。


一百一十六、生产 Origin

不要:

localhost

而是:

真实前端域名

例如:

https://app.example.com

一百一十七、自动发现路由

Gateway 可以结合:

DiscoveryClient

自动为服务建立路由。


一百一十八、开启 Discovery Locator

Spring Cloud 2025.0.x:

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

一百一十九、默认思想

服务:

user-service

可以形成基于:

/serviceId/**

的路由。

目标 URI:

lb://serviceId

一百二十、为什么自动路由很方便

10 个服务:

不用全部手写 Route

一百二十一、为什么课程项目仍推荐显式 Route

因为:

前端 API 路径
不一定等于服务名

还可能需要:

StripPrefix

权限

特定 Filter

版本管理

显式 Route:

更可控

一百二十二、所以自动发现适合什么

快速测试

内部简单环境

统一服务命名规范

一百二十三、生产路由更常见

显式配置关键 Route

一百二十四、路由可以写 Java

除了 YAML,

可以使用:

RouteLocatorBuilder

一百二十五、Java DSL 示例

@Bean
public RouteLocator customRouteLocator(
        RouteLocatorBuilder builder
) {

    return builder
        .routes()
        .route(
            "user-route",
            r -> r
                .path("/api/users/**")
                .filters(
                    f -> f.stripPrefix(1)
                )
                .uri("lb://user-service")
        )
        .build();
}

一百二十六、YAML 和 Java DSL 怎么选

普通固定路由:

YAML

最直观。

复杂动态逻辑:

Java DSL

可能更灵活。


一百二十七、不要把所有 Route 都硬编码 Java

配置型路由:

YAML / 配置中心

通常维护更方便。


一百二十八、动态路由是什么

不重启 Gateway:

增加/修改路由

就叫:

动态路由

一百二十九、为什么需要动态路由

微服务很多:

路由经常变化

如果每次:

改 application.yml
重启 Gateway

比较麻烦。


一百三十、常见实现

Nacos 配置中心

保存 Gateway 路由。

然后 Gateway:

监听配置变化
刷新 RouteDefinition

一百三十一、当前课要做到什么程度

先理解:

动态路由思想

后面若依 Cloud:

会看到真实实现

这里不要求:

手写复杂动态路由监听器

一百三十二、为什么不建议初学者直接复制网上动态路由代码

不同 Gateway 版本:

RouteDefinition API

配置前缀

刷新机制

可能变化。

先把:

静态路由

学扎实。


一百三十三、Gateway 超时

Gateway 自己:

也要有网络超时

因为它负责:

转发下游服务

一百三十四、Connect Timeout

Spring Cloud Gateway 支持:

HTTP Client connect timeout

一百三十五、Response Timeout

还需要考虑:

下游响应等待时间

一百三十六、为什么不能 Gateway 无限等

下游慢:

Gateway 连接资源会被长期占用

最终:

入口也被拖垮

一百三十七、超时层次

Client → Gateway timeout

Gateway → Service timeout

Service → Feign Service timeout

每一层:

都要有边界

一百三十八、超时不要互相矛盾

例如:

Gateway 2 秒

下游 Feign 10 秒

那么外部:

2 秒已经断

内部还可能:

继续处理很久

一百三十九、超时预算

企业会考虑:

End-to-End Timeout Budget

例如用户最多等:

5 秒

再把时间:

分配给各个调用层

一百四十、Gateway Retry Filter

Gateway 支持:

Retry

但一定记:

重试不是越多越好

一百四十一、为什么入口重试更危险

一个用户请求:

Gateway 自动重试 3 次

内部负载瞬间:

放大

如果下游已经过载:

重试风暴

一百四十二、写操作尤其危险

例如:

POST /orders

不能因为:

Gateway 重试

就重复创建订单。


一百四十三、所以 Retry 必须考虑

HTTP Method

幂等性

错误类型

最大次数

退避

一百四十四、Gateway RequestRateLimiter

Gateway 自带:

RequestRateLimiter

过滤器。


一百四十五、WebFlux 常见 Redis RateLimiter

需要:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>
        spring-boot-starter-data-redis-reactive
    </artifactId>
</dependency>

一百四十六、为什么是 redis-reactive

Gateway Server WebFlux:

反应式

所以:

使用 Reactive Redis

而不是在 Filter 中使用阻塞式 RedisTemplate。


一百四十七、RequestRateLimiter 默认拒绝状态

官方默认:

HTTP 429

也就是:

Too Many Requests

一百四十八、KeyResolver

限流必须回答:

“按谁限?”

这就是:

KeyResolver

一百四十九、例如按 IP

@Bean
public KeyResolver ipKeyResolver() {

    return exchange -> {

        InetSocketAddress address =
                exchange
                    .getRequest()
                    .getRemoteAddress();

        String host =
                address == null
                ? "unknown"
                : address
                    .getAddress()
                    .getHostAddress();

        return Mono.just(host);
    };
}

一百五十、例如按用户

可以根据:

认证后的 userId

作为 Key。


一百五十一、为什么比全局 QPS 更精细

总接口:

1000 QPS

但单个用户:

最多 10 QPS

可以防止:

一个用户占满全部容量

一百五十二、Redis RateLimiter Token Bucket

核心配置:

replenishRate

burstCapacity

requestedTokens

一百五十三、replenishRate

每秒补充多少 Token

一百五十四、burstCapacity

Token 桶最大容量

允许:

短时突发

一百五十五、requestedTokens

一个请求:

消耗多少 Token

默认:

1

一百五十六、示例

filters:
  - name: RequestRateLimiter
    args:
      key-resolver: "#{@ipKeyResolver}"
      redis-rate-limiter.replenishRate: 10
      redis-rate-limiter.burstCapacity: 20
      redis-rate-limiter.requestedTokens: 1

一百五十七、这是什么意思

稳定速率:

每秒 10 个

短时桶容量:

20

允许一定:

瞬间突发

一百五十八、Gateway 限流和 Sentinel 限流区别

Gateway RequestRateLimiter:

入口限流

Sentinel:

服务/资源治理

一百五十九、可以组合

Gateway
先挡明显大流量
↓
业务服务 Sentinel
再保护具体资源

一百六十、不要重复无脑限两套

关键是:

容量设计

不是:

组件越多越安全

一百六十一、Gateway + Sentinel

Sentinel 有:

Gateway Adapter

可以针对:

Route

API 分组

做流控。


一百六十二、课程现在为什么不深挖

上一课已经详细学:

Sentinel 核心

这一课重点:

Gateway 本身

只要理解:

Gateway 入口也能接 Sentinel

即可。


一百六十三、下一阶段项目会看到

Gateway
+
Sentinel
+
Nacos

组合治理。


一百六十四、GlobalFilter 做日志要记录什么

推荐:

TraceId

Method

Path

RouteId

Status

Cost

一百六十五、不要默认记录什么

Password

Authorization 完整 Token

银行卡

身份证敏感信息

一百六十六、TraceId

如果客户端没传:

Gateway 生成

然后:

向下游传递

一百六十七、TraceId GlobalFilter 思想

Request
↓
读取 X-Trace-Id
↓
没有则生成 UUID
↓
写入 Request Header
↓
写入日志 MDC
↓
下游继续传播

一百六十八、为什么 TraceId 从 Gateway 生成很合理

因为:

Gateway 是外部请求统一入口

适合作为:

链路起点

一百六十九、但消息队列链路怎么办

后面 MQ 阶段会学:

Trace Context 继续传播

一百七十、Gateway 不应该成为单点故障

如果整个系统:

只有一个 Gateway 实例

Gateway 挂:

所有外部请求都进不来

一百七十一、生产应该

多个 Gateway 实例

再由:

Nginx
云负载均衡
Kubernetes Service

在前面分流。


一百七十二、典型生产结构

Internet
↓
Load Balancer / Nginx
├─ Gateway 1
├─ Gateway 2
└─ Gateway 3
↓
Microservices

一百七十三、Gateway 自己要不要注册 Nacos

如果只是:

最外层由 Nginx 固定代理 Gateway

Gateway 自己是否注册:

根据架构

课程为了统一微服务管理:

可以注册

一百七十四、Gateway 本身可以无状态

推荐:

不要把用户 Session 放 Gateway 单机内存

一百七十五、为什么

多个 Gateway 实例:

请求会随机进入

如果状态只在 Gateway 1:

Gateway 2 不知道

一百七十六、认证状态

可以使用:

JWT

Redis

等共享方案。


一百七十七、Gateway 和 Redis

Redis 可以用于:

Token Session

黑名单

限流

验证码状态

但 Gateway Filter:

尽量使用非阻塞访问方式

一百七十八、Gateway 里调用 Feign 合适吗

通常:

不建议把 Gateway 当业务聚合服务

一百七十九、为什么

Feign 传统调用可能:

引入阻塞式调用模型

Gateway WebFlux:

强调非阻塞

而且:

职责会变重

一百八十、如果需要 API 聚合

可以考虑:

专门 BFF

Aggregator Service

而不是把 Gateway:

写成业务 Service

一百八十一、什么是 BFF

Backend For Frontend:

为特定前端提供聚合接口

例如:

App BFF

Web BFF

一百八十二、Gateway 和 BFF 区别

Gateway:

统一入口和横切治理

BFF:

页面/客户端业务聚合

一百八十三、Gateway 里的数据库访问

不是说:

绝对不能

而是:

应该非常克制

例如每个请求同步查:

MySQL

会影响 Gateway:

高吞吐入口能力

一百八十四、权限数据怎么办

常见:

Token 内携带必要 Claims

或者:

Redis 非阻塞读取轻量会话

复杂权限:

下沉业务服务

一百八十五、白名单匹配不要只 startsWith

例如:

path.startsWith("/api/public")

可能错误放过:

/api/publicFakeDanger

一百八十六、推荐

使用:

PathPattern

AntPathMatcher

或框架对应的安全 Matcher。


一百八十七、路径安全还要防绕过

例如:

重复斜杠

URL 编码

Path Traversal

生产鉴权:

优先使用成熟 Security 机制

一百八十八、课程手写 GlobalFilter 的目的

是让你理解:

认证发生在哪里

不是鼓励生产:

自己重新造整套安全框架

一百八十九、若依 Cloud 后面会看到

成熟脚手架已经:

封装 Gateway 鉴权

Remote Service

Security Context

白名单

到时重点:

理解而不是推倒重写

一百九十、Gateway Actuator

加入:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>
        spring-boot-starter-actuator
    </artifactId>
</dependency>

一百九十一、为什么有用

可以查看:

实际 Route

实际 Filter

Gateway Metrics

一百九十二、查看 Routes

Gateway Actuator 提供:

/actuator/gateway/routes

可以看到:

route_id

predicate

filters

uri

一百九十三、为什么这是排错神器

你以为配置:

已经加载

但实际上:

路由根本没注册

看 Actuator:

马上知道

一百九十四、不要生产直接暴露所有 Actuator

安全原则:

只开放必要 endpoint

内部网络

认证保护

一百九十五、Gateway Metrics

开启 Actuator 后,

Gateway 可以产生:

spring.cloud.gateway.requests

相关指标。


一百九十六、指标可关注

RouteId

Status

Outcome

请求时间

一百九十七、为什么 Gateway Metrics 很重要

Gateway 是:

所有请求入口

它天然适合看到:

哪个服务请求最多

哪个 Route 错误最多

一百九十八、Gateway 访问日志

Reactor Netty 也支持:

Access Log

可以用于:

入口请求日志

但生产:

日志量很大

要合理采集。


一百九十九、Route ID 命名

不要:

route1
route2
route3

推荐:

user-service-route

order-service-route

travel-service-route

二百、为什么 Route ID 很重要

日志和监控:

都会看到 RouteId

名字清晰:

排错更快

二百零一、URI 的三种常见形式

第一:

http://host:port

固定地址。

第二:

https://host

第三方服务。

第三:

lb://service-name

微服务发现。


二百零二、内部服务推荐

lb://service-name

二百零三、外部 API

可以:

https://xxx

但是:

要配置超时
安全
熔断

二百零四、Gateway 与服务上下文路径

如果 Provider:

server:
  servlet:
    context-path: ...

注意:

WebFlux/MVC 服务路径差异

Gateway 路径必须:

和下游真实 URI 对齐

二百零五、为什么建议业务服务路径清晰

例如:

/user/**

和:

/api/user/**

各层一定约定清楚。


二百零六、推荐外部统一前缀

例如:

/api

外部:

/api/users/**

内部:

/users/**

Gateway:

StripPrefix=1

二百零七、为什么这样好

前端:

所有后端 API 都在 /api

Nginx:

也容易代理

二百零八、Web 前端代理

开发阶段 Vue Vite:

也可以 proxy /api 到 Gateway

二百零九、生产

Browser
↓
Nginx
/api/**
↓
Gateway
↓
Microservices

二百一十、Gateway 路由与前端 baseURL

前端 Axios:

baseURL=/api

Gateway:

负责之后所有服务路由

二百一十一、前端不应该知道 service-name

不要让前端请求:

/user-service/users/1

除非这是:

明确设计的公开 API

二百一十二、服务名属于内部架构细节

外部接口:

应该稳定

内部:

可以重构服务名

二百一十三、这叫解耦

外部 API
≠
内部微服务拓扑

二百一十四、Gateway 的路由就是一个解耦层

后面 user-service 改名:

account-service

前端仍可以:

/api/users/**

只要 Gateway Route 修改。


二百一十五、Gateway 返回错误格式

默认错误可能是:

Spring WebFlux 默认错误 JSON

企业项目通常希望:

统一响应格式

二百一十六、哪些错误可以统一

例如:

401 未登录

403 无权限

429 限流

503 服务不可用

二百一十七、但是 Gateway 异常处理是反应式

不能照搬:

@RestControllerAdvice

所有 MVC 异常处理方式。


二百一十八、常见方案

ErrorWebExceptionHandler

WebExceptionHandler

GlobalFilter

结合项目架构处理。


二百一十九、课程先不手写完整统一异常框架

因为下一步:

若依 Cloud

会有现成实现可分析。


二百二十、但你要知道为什么普通 @ControllerAdvice 不一定管得到

因为 Gateway:

路由链路不是普通 MVC Controller 调用链

二百二十一、Gateway WebFlux 常见错误 1

项目同时加入:

spring-boot-starter-web

导致:

Web 应用类型冲突

二百二十二、解决

Gateway WebFlux:

不要随便引入 MVC Web Starter

检查:

mvn dependency:tree

二百二十三、错误 2:旧 Starter 名称

照旧教程:

spring-cloud-starter-gateway

在 2025.x 仍可能遇到:

迁移/弃用提示

新项目:

spring-cloud-starter-gateway-server-webflux

二百二十四、错误 3:旧配置前缀

旧:

spring.cloud.gateway.routes

课程 2025.0.x:

spring.cloud.gateway.server.webflux.routes

二百二十五、为什么旧教程有时还能跑

Spring Cloud 2025.0 引入:

属性迁移机制

有些旧属性:

可能能通过迁移工具过渡

但:

不要把兼容层当新标准

二百二十六、错误 4:lb:// 找不到服务

检查:

Nacos Discovery

LoadBalancer

service name

Namespace

实例健康

二百二十七、错误 5:Gateway 404

检查:

Route 是否加载

Path Predicate

StripPrefix

Provider Controller path

二百二十八、错误 6:Gateway 503

通常:

找不到 lb 服务实例

二百二十九、错误 7:CORS 还是报错

检查:

OPTIONS

Gateway globalcors

allowedOrigins

allowCredentials

下游是否重复加 CORS Header

二百三十、错误 8:GlobalFilter 不执行

检查:

@Component

是否实现 GlobalFilter

是否请求真正经过 Gateway Route

二百三十一、错误 9:Filter 顺序不对

检查:

Ordered#getOrder

二百三十二、错误 10:JWT Header 被伪造

检查:

是否先删除客户端 X-User-Id
再由 Gateway 重新设置

二百三十三、错误 11:Gateway CPU 不高但请求卡死

检查:

有没有阻塞代码

Thread.sleep

阻塞 JDBC

阻塞 Redis

同步远程调用

二百三十四、错误 12:Actuator 看不到 Gateway Routes

检查:

Actuator 依赖

endpoint 是否暴露

Gateway endpoint 权限

二百三十五、错误 13:RequestRateLimiter 不生效

检查:

Reactive Redis

KeyResolver Bean

Route Filter 配置

Redis 连接

Key 是否为空

二百三十六、KeyResolver 返回空 Key

Gateway RequestRateLimiter:

可能拒绝请求

所以:

Key 解析要有明确策略

二百三十七、错误 14:真实客户端 IP 永远是 Nginx

因为 Gateway 前面:

还有代理

getRemoteAddress:

看到的可能是 Nginx IP

二百三十八、这时会涉及

Forwarded

X-Forwarded-For

Trusted Proxies

二百三十九、2025.0.x 的一个重要安全变化

Spring Cloud 2025.0 对:

Forwarded / X-Forwarded

信任策略做了更严格调整。

不要:

无条件相信客户端自己传的 X-Forwarded-For

二百四十、为什么

攻击者可以伪造:

X-Forwarded-For

绕过:

按 IP 限流

IP 白名单

二百四十一、生产要配置可信代理

Gateway 应该知道:

哪些代理地址值得信任

再接受:

Forwarded Header

二百四十二、错误 15:Gateway 日志打印完整 Token

这是:

安全问题

日志:

不要记录完整 Authorization

二百四十三、错误 16:Gateway 里做权限 SQL

表现:

每个请求都同步查数据库

Gateway:

吞吐下降

二百四十四、错误 17:所有业务都塞 GlobalFilter

GlobalFilter 最终:

上千行

说明:

Gateway 职责开始失控

二百四十五、正确拆分

Trace Filter

Auth Filter

Log Filter

职责:

单一

复杂业务:

下沉业务服务

二百四十六、错误 18:Gateway Retry 重试 POST

结果:

重复业务

必须检查:

幂等

二百四十七、错误 19:Gateway 与 Sentinel 阈值冲突

外层:

1000

内层:

10

导致:

大量请求已经进入内部才被挡

需要统一:

容量规划

二百四十八、错误 20:只有一个 Gateway

生产:

形成入口单点

二百四十九、完整微服务架构

                    Internet
                       │
                       ▼
                Nginx / LB
                       │
           ┌───────────┴───────────┐
           ▼                       ▼
       Gateway-1                Gateway-2
           │                       │
           └───────────┬───────────┘
                       ▼
                     Nacos
                       │
        ┌──────────────┼──────────────┐
        ▼              ▼              ▼
  user-service   order-service   travel-service
        │              │              │
        └────── Feign / MQ / DB ──────┘

二百五十、Gateway 在整个体系中的位置

Nacos
→ 服务发现

Feign
→ 服务之间调用

Sentinel
→ 服务稳定性保护

Gateway
→ 外部统一入口

二百五十一、这四个组件现在就串起来了

用户请求:

Vue
↓
Gateway
↓
lb://order-service
↓
Nacos 找实例
↓
Order Service
↓
Feign
↓
User Service
↓
Sentinel 保护调用

二百五十二、IDEA 项目结构建议

spring-cloud-demo
├─ gateway
├─ user-api
├─ user-service
├─ order-service
└─ common

二百五十三、gateway 包结构

gateway
└─ src/main/java
   └─ com.example.gateway
      ├─ GatewayApplication.java
      ├─ config
      ├─ filter
      ├─ handler
      └─ properties

二百五十四、不要给 gateway 建 mapper/service/controller 一大套

除非:

确实有 Gateway 自己的管理需求

否则它不是:

普通 CRUD 服务

二百五十五、学习实验 1:固定 URI 路由

先:

uri=http://localhost:8081

验证:

Gateway 最基础路由

二百五十六、实验 2:改成 lb://

lb://user-service

验证:

Nacos + LoadBalancer

二百五十七、实验 3:多实例

user-service:

8081 + 8083

通过 Gateway 连续访问:

观察实例切换

二百五十八、实验 4:Path Predicate

只匹配:

/api/users/**

二百五十九、实验 5:Method Predicate

只允许:

GET

看 POST:

不匹配路由

二百六十、实验 6:StripPrefix

客户端:

/api/users/1

Provider:

/users/1

设置:

StripPrefix=1

二百六十一、实验 7:AddRequestHeader

Gateway 加:

X-Gateway=demo

Provider 打印 Header。


二百六十二、实验 8:GlobalFilter

打印:

path
method
耗时

二百六十三、实验 9:Filter Order

创建:

Filter A

Filter B

分别:

-100

0

观察:

pre/post 顺序

二百六十四、实验 10:CORS

Vue:

localhost:5173

Gateway:

localhost:8080

验证:

OPTIONS + 真正请求

二百六十五、实验 11:JWT 白名单

白名单:

/login

普通:

/orders

没有 Token:

401

二百六十六、实验 12:伪造 X-User-Id

客户端手工:

X-User-Id: 1

Gateway:

必须删除

再根据 Token:

重建

二百六十七、实验 13:服务下线

关闭 user-service。

Gateway:

访问 lb://user-service

观察:

503

二百六十八、实验 14:Actuator Route

查看:

/actuator/gateway/routes

确认:

真实 Route

二百六十九、实验 15:RequestRateLimiter

按:

IP

限流。

快速请求:

观察 429

二百七十、Gateway 高频面试题 1

问题:

什么是 API Gateway?

答案:

API Gateway 是微服务系统的统一入口。

客户端不需要直接感知内部所有服务地址,
请求先进入 Gateway,
再根据路由规则转发到具体服务。

Gateway 还可以统一处理认证、跨域、日志、
限流和请求头等横切逻辑。

二百七十一、面试题 2:Route 包含什么

答:

Route 是 Gateway 的基本路由单元。

通常包括:
Route ID、
目标 URI、
Predicate 集合、
Filter 集合。

请求满足 Predicate 后,
经过 Filter 处理,
最终转发到 URI。

二百七十二、面试题 3:Predicate 和 Filter 区别

答:

Predicate 决定一个请求是否匹配当前 Route。

Filter 则在 Route 已匹配之后,
对请求或响应进行修改和处理。

可以记成:
Predicate 决定“走不走这条路”,
Filter 决定“路上做什么”。

二百七十三、面试题 4:lb:// 是什么

答:

lb:// 表示目标不是固定 IP,
而是一个逻辑服务名。

Gateway 会结合 Spring Cloud LoadBalancer
和服务发现组件获取实例,
再选择真实 Host 和 Port 转发请求。

二百七十四、面试题 5:Gateway 与 Nacos 如何配合

答:

Nacos 提供服务注册和发现。

Gateway Route 使用 lb://service-name 时,
可以从服务发现体系获得当前可用实例,
再由 LoadBalancer 选择一个实例进行转发。

因此 Gateway 不需要写死内部服务 IP。

二百七十五、面试题 6:Gateway 和 Nginx 区别

答:

Nginx 更偏网络入口、TLS、静态资源和高性能反向代理。

Spring Cloud Gateway 更偏 Spring Cloud 应用层路由,
能够直接整合服务发现、Java 过滤器、认证上下文和微服务治理。

实际项目中两者经常同时存在。

二百七十六、面试题 7:Gateway 为什么用 WebFlux

答:

Gateway Server WebFlux 建立在 Spring WebFlux、
Project Reactor 和 Reactor Netty 上,
适合处理大量网络代理连接。

因此 Filter 使用 Mono 和 ServerWebExchange,
不能简单照搬传统 Servlet 阻塞式编程方式。

二百七十七、面试题 8:为什么 Gateway 不能阻塞

答:

WebFlux Gateway 依赖少量事件循环线程处理大量网络请求。

如果 Filter 中执行 Thread.sleep、
阻塞式 JDBC 或长时间同步操作,
会占用事件循环线程,
导致大量其他请求也无法及时处理。

因此 Gateway 应保持轻量、非阻塞。

二百七十八、面试题 9:GatewayFilter 和 GlobalFilter 区别

答:

GatewayFilter 通常绑定到具体 Route,
只处理该路由请求。

GlobalFilter 会参与所有匹配 Gateway 路由的请求链,
适合统一日志、TraceId 和认证等横切能力。

二百七十九、面试题 10:Filter 顺序怎么判断

答:

GlobalFilter 和 GatewayFilter 会组成过滤器链,
并按照 Ordered 等顺序规则排序。

数值越小通常优先级越高。

高优先级 Filter 在 pre 阶段更早执行,
在 post 阶段更晚执行。

二百八十、面试题 11:StripPrefix 做什么

答:

StripPrefix 会在请求转发下游之前,
删除指定数量的 URL Path Segment。

例如 /api/users/1 使用 StripPrefix=1 后,
下游收到 /users/1。

二百八十一、面试题 12:Gateway 怎么处理跨域

答:

Gateway 可以配置全局 CORS 或 Route 级 CORS。

当所有浏览器请求统一经过 Gateway 时,
通常推荐在 Gateway 集中处理跨域,
避免每个微服务分别配置并产生重复 CORS Header。

二百八十二、面试题 13:为什么 Gateway 可以做统一认证

答:

所有外部请求都经过 Gateway,
所以它适合在入口检查 Token、
识别白名单并解析基础用户身份。

但具体业务权限和数据权限
仍然应该由业务服务进行校验,
不能把 Gateway 作为唯一授权边界。

二百八十三、面试题 14:为什么不能信任客户端 X-User-Id

答:

客户端可以自行伪造 HTTP Header。

如果 Gateway 直接相信客户端提交的 X-User-Id,
攻击者可能冒充其他用户。

正确方式是删除外部传入的内部身份 Header,
根据经过验证的 Token 重新生成可信上下文。

二百八十四、面试题 15:为什么 Gateway 不建议大量查数据库

答:

Gateway 是高并发统一入口,
WebFlux 又强调非阻塞网络处理。

如果每次请求都执行阻塞式数据库查询,
容易拖慢事件循环和整个入口吞吐。

复杂业务权限应尽量下沉业务服务,
Gateway 只保留轻量横切逻辑。

二百八十五、面试题 16:什么是 RequestRateLimiter

答:

RequestRateLimiter 是 Gateway 的请求限流过滤器。

它根据 KeyResolver 计算限流 Key,
再通过 RateLimiter 判断请求是否允许通过。

WebFlux 中常用 RedisRateLimiter,
默认被限流的请求返回 HTTP 429。

二百八十六、面试题 17:Gateway 限流和 Sentinel 区别

答:

Gateway 限流更适合在统一外部入口尽早挡住流量。

Sentinel 更擅长保护内部具体服务和资源,
并提供熔断、慢调用和热点参数等治理能力。

两者可以分层组合,但阈值应统一进行容量设计。

二百八十七、面试题 18:Gateway 为什么会成为单点

答:

如果整个系统只有一个 Gateway 实例,
它一旦宕机,所有外部请求都无法进入微服务系统。

因此生产环境通常部署多个 Gateway 实例,
再由 Nginx、云负载均衡或 Kubernetes Service
在前面进行流量分配。

二百八十八、面试题 19:2025.0.x Gateway 有什么重要变化

答:

Spring Cloud 2025.0 对 Gateway 模块名称和配置前缀进行了迁移。

Server WebFlux 推荐 Starter:
spring-cloud-starter-gateway-server-webflux。

配置前缀也从旧式 spring.cloud.gateway.*
迁移到 spring.cloud.gateway.server.webflux.*。

新项目应直接采用新命名。

二百八十九、面试题 20:Gateway 最大价值是什么

答:

Gateway 最大价值不是简单转发 URL,
而是为微服务提供稳定统一的外部 API 边界。

客户端与内部服务拓扑解耦,
路由、认证、跨域、限流、日志等横切能力
可以集中治理,
内部服务也可以独立扩缩容和演进。

二百九十、Cursor 提示词:创建 Gateway

当前父工程:
JDK17
Spring Boot 3.5.x
Spring Cloud 2025.0.x
Spring Cloud Alibaba 2025.0.x

已有:
user-service
order-service
Nacos

请创建 gateway 模块。

要求:
1. 使用 spring-cloud-starter-gateway-server-webflux
2. 不使用旧 spring-cloud-starter-gateway 作为新项目主方案
3. 不引入 spring-boot-starter-web
4. Gateway 注册 Nacos
5. 显式引入 Spring Cloud LoadBalancer
6. gateway 端口 8080
7. 不加入业务 Mapper/数据库代码

二百九十一、Cursor 提示词:Route

请给 Gateway 添加两条显式 Route:

/api/users/**
→ lb://user-service

/api/orders/**
→ lb://order-service

要求:
1. 使用 Spring Cloud 2025.0.x 新配置前缀
spring.cloud.gateway.server.webflux.routes
2. 每条 Route 有清晰 id
3. 使用 Path Predicate
4. 使用 StripPrefix=1
5. 不写死 localhost:8081/8082
6. 给出最终请求路径变化示意

二百九十二、Cursor 提示词:旧配置迁移

请审查 gateway application.yml。

当前技术栈是 Spring Cloud 2025.0.x。

检查是否仍使用:
spring.cloud.gateway.routes
spring.cloud.gateway.globalcors
spring-cloud-starter-gateway-server

如果有,请说明对应的新:
spring.cloud.gateway.server.webflux.*
spring-cloud-starter-gateway-server-webflux

先输出迁移清单,不要无脑修改其他配置。

二百九十三、Cursor 提示词:Path 404 排查

Gateway 返回 404。

请按以下链路分析:
1. 客户端原始 Path
2. Route Path Predicate
3. StripPrefix / RewritePath 处理后 Path
4. lb:// service-name
5. Provider 实际 Controller @RequestMapping
6. Nacos 是否存在实例

请把每一步的“实际值”列出来,
不要只说检查配置。

二百九十四、Cursor 提示词:GlobalFilter

请实现一个轻量 Gateway GlobalFilter。

要求:
1. 使用 WebFlux GlobalFilter
2. 使用 ServerWebExchange
3. 记录 method/path/status/cost
4. 实现 Ordered
5. 不使用 Thread.sleep
6. 不使用阻塞 JDBC
7. 不打印完整 Authorization Token
8. 说明 pre/post 执行顺序

二百九十五、Cursor 提示词:JWT 鉴权

请设计 Gateway JWT 入口鉴权。

要求:
1. 白名单配置化
2. 非白名单检查 Authorization
3. 校验 Token
4. 从 Token 获取 userId
5. 删除客户端原始 X-User-Id
6. 重新写入可信 X-User-Id
7. Token 无效返回 401
8. Gateway 只做基础认证
9. 业务 RBAC/DataScope 仍由业务服务处理
10. 使用 WebFlux API,不使用 HttpServletRequest

二百九十六、Cursor 提示词:CORS

请按 Spring Cloud 2025.0.x Gateway Server WebFlux
配置全局 CORS。

要求:
1. 使用 spring.cloud.gateway.server.webflux.globalcors
2. 支持 localhost:5173
3. GET/POST/PUT/DELETE/OPTIONS
4. 处理预检请求
5. allowCredentials=true 时不要 allowedOrigins="*"
6. 检查下游服务是否重复添加 CORS Header

二百九十七、Cursor 提示词:限流

请给 Gateway 的 order Route 加 RequestRateLimiter。

要求:
1. 使用 Reactive Redis
2. 创建按 IP KeyResolver
3. replenishRate=10
4. burstCapacity=20
5. requestedTokens=1
6. 说明 Token Bucket 原理
7. 被限流返回 429
8. 不使用阻塞 RedisTemplate

二百九十八、Cursor 提示词:安全审查

请审查 Gateway 安全风险。

重点检查:
1. 是否信任客户端 X-User-Id
2. 是否记录完整 Token
3. 白名单是否过宽
4. Actuator 是否公网暴露
5. CORS 是否 allowedOrigins=* + credentials
6. 是否信任任意 X-Forwarded-For
7. 是否有未限制的大文件请求
8. 是否有 Gateway Retry 重试非幂等 POST
9. 下游服务是否仍能直接公网访问
10. 是否把完整业务授权只放 Gateway

二百九十九、Cursor 提示词:WebFlux 阻塞审查

请审查 gateway 模块是否存在阻塞代码。

查找:
1. Thread.sleep
2. JDBC / MyBatis
3. RestTemplate
4. 阻塞 RedisTemplate
5. Future.get
6. 同步文件 IO
7. 长时间 CPU 操作
8. 在 GlobalFilter 中复杂数据库权限查询

说明每处为什么可能阻塞 Reactor Netty。
不要直接全部改,先分风险等级。

三百、Cursor 提示词:Gateway + Nacos

请检查 Gateway 的 lb:// 路由为什么返回 503。

按顺序检查:
1. gateway 是否注册/连接 Nacos
2. Provider 是否注册
3. service-name 是否完全一致
4. Namespace 是否一致
5. Provider 是否健康
6. spring-cloud-starter-loadbalancer 是否存在
7. Route URI 是否 lb://xxx
8. mvn dependency:tree 是否存在版本冲突

三百零一、本章知识树

Spring Cloud Gateway
│
├─ Core
│  ├─ Route
│  ├─ Predicate
│  ├─ GatewayFilter
│  └─ GlobalFilter
│
├─ Routing
│  ├─ Path
│  ├─ Method
│  ├─ Header
│  ├─ Query
│  ├─ Host
│  ├─ RemoteAddr
│  └─ Time
│
├─ Service Discovery
│  ├─ Nacos
│  ├─ lb://
│  ├─ LoadBalancer
│  └─ Discovery Locator
│
├─ Filter
│  ├─ StripPrefix
│  ├─ RewritePath
│  ├─ PrefixPath
│  ├─ AddRequestHeader
│  ├─ RemoveRequestHeader
│  ├─ RequestSize
│  └─ Retry
│
├─ WebFlux
│  ├─ Reactor Netty
│  ├─ Mono
│  ├─ ServerWebExchange
│  ├─ ServerHttpRequest
│  └─ Non-blocking
│
├─ Security
│  ├─ JWT
│  ├─ White List
│  ├─ Internal Headers
│  ├─ 401
│  ├─ CORS
│  └─ Trusted Proxy
│
├─ Rate Limit
│  ├─ RequestRateLimiter
│  ├─ KeyResolver
│  ├─ Reactive Redis
│  └─ Token Bucket
│
├─ Observability
│  ├─ TraceId
│  ├─ Logging
│  ├─ Metrics
│  └─ Actuator Routes
│
└─ High Availability
   ├─ Multiple Gateway Instances
   ├─ Nginx/LB
   ├─ Timeout
   └─ Sentinel

三百零二、四大组件总复习

Nacos
→ 服务在哪里?

Feign
→ 服务之间怎么方便调用?

Sentinel
→ 服务怎么防止被流量和下游故障拖垮?

Gateway
→ 外部请求统一从哪里进入?

三百零三、完整调用流程

Vue
↓
Nginx
↓
Gateway
│
├─ CORS
├─ TraceId
├─ JWT
├─ Route
├─ Rate Limit
│
↓ lb://order-service
Nacos + LoadBalancer
↓
Order Service
↓
Feign
↓
User Service
↓
Sentinel

三百零四、本章最重要的 20 条原则

1. Gateway 是统一入口,不是超级业务服务

2. 2025.0.x 新项目使用 gateway-server-webflux Starter

3. 2025.0.x 配置优先使用 spring.cloud.gateway.server.webflux.*

4. Gateway Server WebFlux 不要无脑混入 spring-boot-starter-web

5. Route = id + uri + predicates + filters

6. Predicate 决定请求是否匹配

7. Filter 决定匹配后如何处理

8. 内部微服务 URI 优先 lb://service-name

9. Nacos 负责发现实例,LoadBalancer 负责选择实例

10. StripPrefix 最容易导致路径 404,必须画路径变化

11. GlobalFilter 适合统一横切逻辑

12. WebFlux GlobalFilter 中不要执行阻塞操作

13. Gateway 可做基础认证,但不能替代业务授权

14. 永远不要信任客户端伪造的内部身份 Header

15. 所有浏览器流量统一 Gateway 时优先统一配置 CORS

16. Gateway Retry 必须考虑幂等

17. Gateway 限流越早拒绝,内部资源浪费越少

18. Gateway 也需要超时、监控和高可用

19. Actuator Routes 是 Gateway 排错的重要工具

20. 外部 API 与内部微服务拓扑应该解耦

三百零五、本章最终验收

学完以后,你应该能独立回答和完成:

1. Gateway 为什么存在

2. Gateway 和 Nginx 区别

3. Gateway Server WebFlux 是什么

4. 为什么不能混用 Servlet 阻塞思维

5. 2025.0.x Starter 新名称

6. 2025.0.x Gateway 新配置前缀

7. 创建 gateway Maven 模块

8. Gateway 注册 Nacos

9. Route 四大组成

10. Path Predicate

11. Method Predicate

12. Header Predicate

13. Query Predicate

14. lb://service-name

15. Nacos + LoadBalancer 路由

16. StripPrefix

17. RewritePath

18. PrefixPath

19. AddRequestHeader

20. RemoveRequestHeader

21. GatewayFilter

22. GlobalFilter

23. Ordered

24. pre/post 顺序

25. ServerWebExchange

26. Mono<Void>

27. WebFlux 非阻塞要求

28. CORS

29. OPTIONS 预检

30. JWT 基础入口鉴权

31. 白名单

32. 防止 X-User-Id 伪造

33. RequestRateLimiter

34. KeyResolver

35. Reactive Redis

36. Token Bucket

37. Gateway + Sentinel 分层治理

38. Gateway Timeout

39. Gateway Retry 幂等风险

40. Actuator 查看路由

41. Gateway 多实例高可用

42. Trusted Proxy 与 X-Forwarded-For 风险

43. 动态路由基本思想

44. Discovery Locator

45. Gateway 为什么不能做大量数据库业务

三百零六、第四阶段前四课已经形成完整基础

到这里已经完成:

第 1 课
微服务体系架构

第 2 课
Nacos + Feign

第 3 课
Sentinel

第 4 课
Gateway

你已经可以把最基础的 Spring Cloud Alibaba 微服务链路串起来:

Gateway
↓
Nacos
↓
LoadBalancer
↓
Service
↓
Feign
↓
Service
↓
Sentinel

三百零七、下一课

按照课程表,下一篇进入:

《若依 Cloud 框架部署与代码修改器使用》

下一课最大的变化是:

不再从零手写微服务骨架

而是开始阅读:

一个已经搭好的企业级微服务脚手架

重点会讲:

若依 Cloud 是什么

为什么它不是前面的 RuoYi-Vue 单体版

项目模块结构

ruoyi-gateway

ruoyi-auth

ruoyi-system

ruoyi-modules

ruoyi-api

ruoyi-common

Nacos 配置

Gateway 路由

Feign Remote Service

Sentinel

Redis

登录认证链

权限传递

服务启动顺序

数据库初始化

Nacos 配置导入

前后端启动

代码修改器/生成器如何使用

如何增加自己的业务微服务

哪些框架代码不要乱改

也就是说:

前四课学的是“零件”

下一课开始:

看一辆已经组装好的车

这会非常重要,因为你会真正明白:

Nacos、Feign、Sentinel、Gateway
在一个完整脚手架项目里
到底是怎么组织起来的