目录

一、核心认知:Gateway 解决什么问题

1.1 本质定位

1.2 和 Netflix Zuul 的核心区别

二、核心架构与执行流程

2.1 三大核心概念

2.2 完整执行流程

三、Spring Cloud 整合实战

3.1 引入依赖

3.2 基础路由配置

示例 1:固定地址转发(直连模式)

示例 2:服务发现模式(生产标准用法)

四、核心功能详解

4.1 常用断言工厂

1. Path 路径断言(最常用)

2. After / Before 时间断言

3. Between 时间区间断言

4. Header 请求头断言

5. Cookie 断言

6. Query 请求参数断言

7. Method 请求方法断言

8. Host 域名断言

9. Weight 权重断言

多断言组合示例(生产常用写法)

4.2 常用局部过滤器

1. StripPrefix 路径截取(最高频)

2. AddRequestHeader / AddResponseHeader

3. PrefixPath

4. Retry 重试

4.3 全局过滤器与自定义鉴权

实战:自定义全局鉴权过滤器

五、底层核心原理

5.1 请求处理全链路源码拆解

第一步:DispatcherHandler 分发请求

第二步:RoutePredicateHandlerMapping 匹配路由

第三步:FilteringWebHandler 执行过滤器链

5.2 路由加载与动态刷新

六、生产级高级特性

6.1 网关限流(Redis 令牌桶实现)

第一步:引入依赖

第二步:配置限流规则

第三步:定义限流维度

6.2 整合 Sentinel 限流熔断

6.3 全局跨域配置

6.4 全局异常处理

七、真实项目踩坑汇总

1. 启动失败:和 Spring MVC 冲突

2. StripPrefix 路径截取错误

3. 请求体只能读取一次

4. 跨域配置不生效

5. 大文件上传超时 / 失败

6. 过滤器执行顺序混乱

7. 服务名路由 404

八、复习速记


(基于 Spring Cloud Alibaba 2.2.9.RELEASE + Spring Cloud Gateway 2.2.9.RELEASE 整理,覆盖实战、原理、踩坑)

一、核心认知:Gateway 解决什么问题

1.1 本质定位

Spring Cloud Gateway 是 Spring 官方推出的第二代微服务网关,替代了已经停止维护的 Netflix Zuul 1.x。它基于 Spring 5、Spring Boot 2、WebFlux、Reactor、Netty 构建,全程异步非阻塞,性能比传统 Servlet 架构的 Zuul 高出数倍。

在微服务架构里,网关是所有外部请求的统一入口,相当于整个系统的「大门保安」,所有客户端不直接访问后端服务,全部先走网关。核心承担这几类工作:

  • 路由转发:根据请求路径、参数等规则,把请求转发到对应的后端服务
  • 统一管控:鉴权、日志、限流、熔断、跨域、黑白名单等通用逻辑,不用每个服务都写一遍
  • 服务透明化:对外只暴露网关地址,后端服务集群、扩缩容对前端完全透明
  • 流量治理:灰度发布、流量染色、版本路由等高级流量调度能力

前提:Gateway 底层是响应式 WebFlux + Netty,不是 Servlet 架构,因此不能和 spring-boot-starter-web 同时引入,否则会直接启动失败,这是最高频的踩坑点。

1.2 和 Netflix Zuul 的核心区别

对比维度 Spring Cloud Gateway Netflix Zuul 1.x
底层架构 Netty + WebFlux,响应式非阻塞 Servlet,同步阻塞
线程模型 少量线程即可支撑高并发,资源占用低 每个请求一个线程,高并发下线程膨胀严重
性能 官方测试是 Zuul 的 2~4 倍 性能较低,易成为瓶颈
维护主体 Spring 官方持续迭代 Netflix 已停止维护
功能丰富度 断言、过滤器、限流、熔断、灰度等全内置 基础路由过滤,高级功能需自己扩展
异步支持 原生支持异步、响应式编程 仅同步模式

面试考点:Gateway 性能高的核心原因是「非阻塞 + 事件驱动」的 Netty 模型,不用为每个请求分配独立线程,大大降低了线程切换和内存开销,高并发下优势极其明显。

二、核心架构与执行流程

2.1 三大核心概念

Gateway 所有功能都围绕三个核心概念展开:

  1. Route(路由) 网关的最小配置单元,一条路由对应一套转发规则。由 ID、目标 URI、断言集合、过滤器集合四部分组成。请求匹配上某条路由后,就会被转发到对应的目标地址。

  2. Predicate(断言) 路由的匹配条件,用来判断请求能不能命中当前路由。支持多种匹配维度:请求路径、请求时间、请求头、Cookie、请求参数、请求方法等。多个断言可以组合使用,全部满足才算匹配成功。 本质是 Java 8 的 Predicate 函数式接口,输入是 ServerWebExchange,输出是 boolean。

  3. Filter(过滤器) 请求转发的前后置处理器,可以修改请求、响应,也可以做鉴权、限流、日志等逻辑。分为两类:

    • 局部过滤器 GatewayFilter:只对配置了它的单条路由生效
    • 全局过滤器 GlobalFilter:对所有路由都生效,无需单独配置

2.2 完整执行流程

流程里两个关键点:

  1. 路由匹配是按顺序匹配,命中第一条就停止,所以配置路由时要把精确的规则放前面,宽泛的放后面,避免被宽泛路由提前拦截。
  2. 过滤器是责任链模式,前置逻辑正序执行,后置逻辑倒序执行,和 Servlet Filter、Spring MVC 拦截器的执行规律一致。

三、Spring Cloud 整合实战

3.1 引入依赖

网关服务单独创建项目,引入网关和服务发现依赖。版本由 Spring Cloud Alibaba 父工程统一管理,无需单独指定。

<dependencies>
    <!-- Nacos 服务发现 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

    <!-- Spring Cloud Gateway 网关 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
</dependencies>

提醒:绝对不能引入 spring-boot-starter-web,Gateway 依赖的是 spring-boot-starter-webflux,两者冲突会直接启动报错。如果项目里间接引入了 web,一定要排除掉。

3.2 基础路由配置

路由配置支持 YAML 配置和 Java 代码配置两种,生产环境 90% 都用 YAML,直观易维护。

示例 1:固定地址转发(直连模式)

适合后端地址固定的场景,直接写死目标 IP 和端口。

server:
  port: 9000

spring:
  cloud:
    gateway:
      routes:
        - id: user-service-route  # 路由唯一ID,自定义即可
          uri: http://127.0.0.1:8081  # 转发目标地址
          predicates:
            - Path=/api/user/**  # 断言:路径匹配/api/user开头的请求
          filters:
            - StripPrefix=1  # 过滤器:去掉第一层路径前缀/api

效果:请求 http://网关地址:9000/api/user/1,会被转发到 http://127.0.0.1:8081/user/1

示例 2:服务发现模式(生产标准用法)

结合 Nacos 服务注册中心,用服务名替代固定地址,自动结合 LoadBalancer 做负载均衡,后端扩缩容不用改网关配置。

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: false  # 关闭自动按服务名生成路由,手动配置更可控
      routes:
        - id: user-service-route
          uri: lb://user-service  # lb:// 开头代表从注册中心找服务,做负载均衡
          predicates:
            - Path=/api/user/**
          filters:
            - StripPrefix=1

        - id: order-service-route
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
          filters:
            - StripPrefix=1

lb:// 是核心前缀,告诉网关启用负载均衡客户端,从注册中心拉取 user-service 的实例列表,按负载均衡策略选择实例转发。

四、核心功能详解

4.1 常用断言工厂

Gateway 内置了十几种断言工厂,覆盖绝大多数匹配场景,几个最常用的如下:

断言类型 作用 配置示例
Path 按请求路径匹配,最常用 - Path=/api/user/**,/api/admin/**
After 指定时间之后的请求才匹配 - After=2025-01-01T00:00:00+08:00[Asia/Shanghai]
Before 指定时间之前的请求才匹配 - Before=2025-06-01T00:00:00+08:00[Asia/Shanghai]
Header 请求头包含指定字段才匹配 - Header=token, [a-z0-9]+
Cookie Cookie 包含指定字段才匹配 - Cookie=userId, \\d+
Query 请求参数包含指定字段才匹配 - Query=source, h5
Method 指定请求方法才匹配 - Method=GET,POST

多个断言是「与」的关系,必须全部满足才能命中路由。

1. Path 路径断言(最常用)

根据请求的 URI 路径匹配,支持 ** 通配符匹配多级路径,* 匹配单级路径。

spring:
  cloud:
    gateway:
      routes:
        - id: user-service-route
          uri: lb://user-service
          predicates:
            # 匹配所有 /api/user 开头的请求,例如 /api/user/1、/api/user/list/page
            - Path=/api/user/**
          filters:
            - StripPrefix=1

多路径写法:用逗号分隔,命中任意一个路径即可

predicates:
  - Path=/api/user/**,/api/member/**
2. After / Before 时间断言
  • After:只有在指定时间之后发起的请求才匹配
  • Before:只有在指定时间之前发起的请求才匹配

时间格式必须是 ZonedDateTime 标准格式,带时区。

spring:
  cloud:
    gateway:
      routes:
        - id: activity-route
          uri: lb://activity-service
          predicates:
            # 2025年11月1日 0点 之后发起的请求才命中该路由
            - After=2025-11-01T00:00:00+08:00[Asia/Shanghai]

场景:大促活动提前配置好路由,到时间自动生效,不用半夜发版;或者活动结束自动下线,切回普通接口。

3. Between 时间区间断言

请求时间在两个时间点之间才匹配,适合限时活动。

predicates:
  # 仅在双十一当天 0点到24点之间生效
  - Between=2025-11-11T00:00:00+08:00[Asia/Shanghai], 2025-11-12T00:00:00+08:00[Asia/Shanghai]
4. Header 请求头断言

根据请求头中的字段匹配,支持正则表达式校验值。

spring:
  cloud:
    gateway:
      routes:
        - id: gray-route
          uri: lb://user-service-gray
          predicates:
            - Path=/api/user/**
            # 请求头必须包含 version 字段,且值为 v2 才命中灰度路由
            - Header=version, v2

正则写法:值满足正则规则才匹配

predicates:
  # token 请求头的值必须是纯数字才匹配
  - Header=token, \d+

实战场景:灰度发布时,前端在请求头带上版本号,网关把流量切到灰度集群。

5. Cookie 断言

根据 Cookie 中的字段匹配,同样支持正则。

predicates:
  # Cookie 中必须有 userType 字段,且值为 vip 才命中
  - Cookie=userType, vip
实战场景:根据用户等级分流,VIP 用户走专属服务集群。
6. Query 请求参数断言

根据 URL 后面的查询参数匹配。

predicates:
  # 请求参数中必须包含 source,且值为 h5 才命中
  - Query=source, h5

只判断存在、不校验值:

predicates:
  # 只要有 debug 参数就命中,不管值是什么
  - Query=debug
7. Method 请求方法断言

只允许指定的 HTTP 方法命中路由。

predicates:
  # 只有 GET 和 POST 请求才命中
  - Method=GET,POST

实战场景:读写分离路由,写操作单独走一条高可用链路。

8. Host 域名断言

根据请求的 Host 域名匹配。

predicates:
  # 只有通过 api.example.com 域名访问才命中
  - Host=api.example.com

多域名写法:

predicates:
  - Host=api.example.com, m.example.com

场景:一套网关集群承载多个业务域名,按域名分发到不同业务服务。

9. Weight 权重断言

按权重比例把流量分配到不同的目标服务,是灰度发布、流量切分的核心手段。

spring:
  cloud:
    gateway:
      routes:
        - id: user-v1-route
          uri: lb://user-service-v1
          predicates:
            - Path=/api/user/**
            # 同组名 weight-group,权重 80
            - Weight=user-weight-group, 80

        - id: user-v2-route
          uri: lb://user-service-v2
          predicates:
            - Path=/api/user/**
            # 同组名,权重 20
            - Weight=user-weight-group, 20

效果:80% 的流量走 v1 版本,20% 的流量走 v2 版本;两条路由必须用同一个分组名才能按权重分配流量。

多断言组合示例(生产常用写法)

同一条路由下多个断言同时生效,全部满足才命中:

spring:
  cloud:
    gateway:
      routes:
        - id: vip-activity-route
          uri: lb://activity-service
          predicates:
            # 路径匹配
            - Path=/api/activity/seckill
            # 活动时间内
            - Between=2025-11-11T00:00:00+08:00[Asia/Shanghai], 2025-11-11T23:59:59+08:00[Asia/Shanghai]
            # VIP用户Cookie
            - Cookie=userLevel, vip
            # POST请求
            - Method=POST

只有同时满足「路径正确 + 活动时间内 + VIP 用户 + POST 方法」四个条件,才会命中这条秒杀路由。

4.2 常用局部过滤器

局部过滤器只对当前路由生效,需要配置在对应路由的 filters 下。

1. StripPrefix 路径截取(最高频)

作用:去掉 URL 中前面的 N 层路径前缀。

filters:
  - StripPrefix=2

请求路径 /api/biz/user/1,截取 2 层前缀后,转发到后端的路径变成 /user/1

为什么要截前缀:网关层用前缀区分不同服务,后端服务本身不需要这个前缀,所以要截掉,是最常用的过滤器,几乎每条路由都会配。

2. AddRequestHeader / AddResponseHeader

作用:给请求 / 响应添加自定义头,常用于传递网关标识、灰度标记、链路追踪 ID。

filters:
  - AddRequestHeader=gateway-route, user-service
  - AddResponseHeader=system-version, v1.2.0
3. PrefixPath

作用:给请求路径统一添加前缀,和 StripPrefix 相反。

filters:
  - PrefixPath=/inner

请求路径 /user/1,转发时变成 /inner/user/1

4. Retry 重试

作用:后端调用失败时自动重试,适合幂等接口。

filters:
  - name: Retry
    args:
      retries: 2  # 重试次数
      statuses: BAD_GATEWAY, SERVICE_UNAVAILABLE  # 触发重试的状态码
      methods: GET  # 只对GET请求重试,避免写接口重复执行

4.3 全局过滤器与自定义鉴权

全局过滤器对所有路由生效,不用每条路由单独配置,适合做全局限流、鉴权、日志、请求 ID 生成等通用逻辑。

内置的核心全局过滤器:

  • LoadBalancerClientFilter:解析 lb:// 前缀,调用负载均衡器选择实例,把服务名替换成实际 IP 端口
  • NettyRoutingFilter:真正发起 HTTP 请求转发的过滤器,是过滤器链的最后一步
  • NettyWriteResponseFilter:把后端返回的响应写回给客户端
实战:自定义全局鉴权过滤器

所有请求必须携带有效 token 才能放行,是网关最常用的能力之一。

/**
 * 全局鉴权过滤器
 * 实现GlobalFilter + Ordered接口,所有路由自动生效
 */
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();

        // 白名单路径直接放行,比如登录、验证码接口
        // 这种可以放配置文件里面动态读取
        if (path.startsWith("/api/user/login") || path.startsWith("/api/common/captcha")) {
            return chain.filter(exchange);
        }

        // 从请求头获取token
        List<String> tokenList = request.getHeaders().get("token");
        if (tokenList == null || tokenList.isEmpty()) {
            // 没有token,返回401未授权
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
            
            String result = "{\"code\":401,\"message\":\"未登录,请先登录\"}";
            DataBuffer buffer = response.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8));
            return response.writeWith(Mono.just(buffer));
        }

        String token = tokenList.get(0);
        // 这里可以调用鉴权服务、校验token有效性
        // 校验通过,把用户ID放到请求头里传给下游服务
        ServerHttpRequest newRequest = request.mutate()
                .header("userId", parseUserIdFromToken(token))
                .build();

        return chain.filter(exchange.mutate().request(newRequest).build());
    }

    @Override
    public int getOrder() {
        // 顺序值越小越先执行,鉴权要放在最前面,比限流、转发都早
        return -100;
    }

    private String parseUserIdFromToken(String token) {
        // 实际项目里解析JWT或者调用用户中心校验
        return "1001";
    }
}

返回响应的时候要用响应式的 Mono 写法,不能像 MVC 里直接 PrintWriter 输出,因为 Gateway 是 WebFlux 异步模型。

五、底层核心原理

5.1 请求处理全链路源码拆解

一次请求从进入网关到转发出去,核心经过三个核心组件,按顺序执行:

第一步:DispatcherHandler 分发请求

它是 WebFlux 的总请求分发器,相当于 Spring MVC 里的 DispatcherServlet。所有请求进来先到这里,它会遍历所有的 HandlerMapping,找到能处理当前请求的那个。 Gateway 自定义的 RoutePredicateHandlerMapping 就是其中一个 HandlerMapping,专门负责路由匹配。

第二步:RoutePredicateHandlerMapping 匹配路由

核心方法 getHandlerInternal,做两件事:

  1. RouteLocator 里拿到所有已加载的路由定义
  2. 按配置顺序遍历每条路由,调用路由上所有断言的 test() 方法
  3. 找到第一条全部断言都匹配成功的路由,包装后返回给 DispatcherHandler

为什么是第一条匹配就返回?因为路由是有优先级的,配置顺序就是优先级顺序,前面的优先级高,匹配上就不再往后找,这也是为什么精确路由要写在宽泛路由前面的原因。

第三步:FilteringWebHandler 执行过滤器链

拿到匹配的路由后,由 FilteringWebHandler 来执行,核心逻辑:

  1. 收集所有全局过滤器 + 当前路由的所有局部过滤器
  2. order 值从小到大排序,组成完整的过滤器链
  3. 依次执行每个过滤器的 filter 方法,形成责任链调用
  4. 最终执行到 NettyRoutingFilter,通过 Netty 客户端向后端服务发起真实的 HTTP 请求
  5. 后端返回响应后,按倒序执行每个过滤器的后置逻辑

5.2 路由加载与动态刷新

所有路由定义都由 RouteDefinitionLocator 负责加载,这是一个顶层接口,有多个实现:

  • PropertiesRouteDefinitionLocator:从 application.yml 配置文件读取路由
  • DiscoveryClientRouteDefinitionLocator:从注册中心自动生成路由(开启动态路由时用)
  • 支持从 Nacos、Apollo 等配置中心加载路由,实现动态刷新

动态刷新原理: Gateway 支持监听配置变更事件,收到变更后会重新调用 RouteLocator 加载路由,更新内存中的路由缓存,不用重启网关就能生效,生产环境做路由变更非常方便。

六、生产级高级特性

6.1 网关限流(Redis 令牌桶实现)

Gateway 内置了基于 Redis 的令牌桶限流过滤器,生产环境最常用。原理是每秒生成固定数量的令牌放到桶里,每个请求拿一个令牌,拿不到就被限流。

第一步:引入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
第二步:配置限流规则
spring:
  redis:
    host: 127.0.0.1
    port: 6379
  cloud:
    gateway:
      routes:
        - id: user-service-route
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 100  # 每秒生成令牌数(平均QPS)
                redis-rate-limiter.burstCapacity: 200 # 令牌桶最大容量(峰值QPS)
                key-resolver: "#{@ipKeyResolver}"     # 限流维度的Bean
第三步:定义限流维度
/**
 * 按IP地址限流
 * 也可以按用户ID、按接口路径等自定义维度
 */
@Bean
public KeyResolver ipKeyResolver() {
    return exchange -> Mono.just(
            exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
    );
}

在 Gateway 中,如果配置了 RequestRateLimiter 过滤器,它就需要知道“到底把谁算作一个用户”。

  • 如果不配置 KeyResolver:Gateway 会把所有请求的流量混在一起统计(比如限流 100 QPS,是所有用户加起来只能有 100 QPS)。

  • 配置了这段代码后:Gateway 会以 IP 为维度来统计流量。

举个例子
你配置了全局限流为 10 次/秒

  • 用户 A(IP:192.168.1.1)一秒内请求了 10 次 → 触发限流,返回 429。

  • 用户 B(IP:192.168.1.2)在同一秒内也请求了 10 次 → 不受影响,正常通过。
    因为 KeyResolver 返回的是 IP,Gateway 内部会把这个 IP 作为 Redis(或本地内存)计数器的 Key,各个 IP 的计数器是互相隔离的。

6.2 整合 Sentinel 限流熔断

如果需要更灵活的限流、熔断降级,可以直接整合 Sentinel,和微服务体系统一管控。 引入 sentinel-spring-cloud-gateway-adapter 依赖后,配置网关流控规则、降级规则,支持按路由、按 API 分组限流,支持慢调用熔断、异常比例熔断,和之前 Sentinel 笔记的逻辑完全一致。

6.3 全局跨域配置

网关层统一处理跨域,后端服务就不用再配了,推荐用代码配置,比 YAML 兼容性更好。

@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration config = new CorsConfiguration();
        // 允许的跨域源,生产环境不要写*,指定具体域名
        config.addAllowedOriginPattern("*");
        // 允许所有请求头
        config.addAllowedHeader("*");
        // 允许所有请求方法
        config.addAllowedMethod("*");
        // 允许携带凭证(Cookie)
        config.setAllowCredentials(true);
        // 预检请求缓存时间,3600秒内不用再发预检请求
        config.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(source);
    }
}

6.4 全局异常处理

Gateway 是 WebFlux 架构,不能用 Spring MVC 的 @RestControllerAdvice 处理异常,需要自定义异常处理器。

默认网关报错会返回白标签错误页,很不友好,自定义后统一返回 JSON 格式,和后端服务的返回格式对齐。核心是实现 ErrorWebExceptionHandler 接口,重写异常处理逻辑,对限流异常、404、500 等做不同的提示。

七、真实项目踩坑汇总

1. 启动失败:和 Spring MVC 冲突

现象:启动时报错 Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway。 原因:引入了 spring-boot-starter-web,或者间接依赖里带了 web。

解决:排除 web 依赖,确保只有 spring-boot-starter-webflux。如果是公共依赖包带进来的,用 exclusion 排除掉。

2. StripPrefix 路径截取错误

现象:转发后后端报 404,抓包发现路径不对。

原因:StripPrefix 的数值和实际前缀层数不匹配,比如有两层前缀只截了一层。

解决:数清楚网关路径有几层前缀,严格对应;复杂路径可以用 Postman 先测单条路由,确认转发路径正确。

3. 请求体只能读取一次

现象:自定义过滤器里读取了请求体做日志、参数校验,转发到后端后后端拿不到请求体。

原因:WebFlux 的请求体是数据流,只能消费一次,读取过就没了。

解决:用 ServerHttpRequestDecorator 包装请求,把请求体缓存起来,下游读取的时候重新生成流。注意大文件请求不要缓存,会占内存。

4. 跨域配置不生效

现象:前端浏览器报跨域错误,明明配了 CorsFilter。 常见原因:

  • 过滤器顺序太低,被其他过滤器抢先返回了错误响应,没走到跨域过滤器
  • 同时配置了 YAML 跨域和代码跨域,配置冲突
  • 后端服务也配了跨域,重复设置导致浏览器拦截 解决:代码配置设置最高优先级,只在网关层做跨域,后端服务关闭跨域配置。

5. 大文件上传超时 / 失败

现象:上传几兆的文件就报错或者超时。

原因:Gateway 默认对请求体大小有限制,内存缓冲区默认不大。

解决:调大 spring.codec.max-in-memory-size 参数,同时调整 Netty 的超时时间;超大文件建议走对象存储直传,不要走网关。

6. 过滤器执行顺序混乱

现象:有时候鉴权在限流后面,有时候又在前面,行为不稳定。

原因:自定义过滤器没指定 order,默认顺序不可控。

解决:所有自定义过滤器都实现 Ordered 接口,显式指定 order 值。约定好顺序规范:鉴权(-100)→ 日志(-50)→ 限流(0)→ 业务过滤器 → 转发(100+)。

7. 服务名路由 404

现象:配置了 lb://user-service 但报 404 或者找不到服务。 排查顺序:

  1. 检查服务有没有正确注册到 Nacos,服务名是不是完全一致(大小写敏感)
  2. 检查网关有没有引入 nacos discovery 依赖
  3. 检查 uri 是不是 lb:// 开头,不是 http://
  4. 检查路径截取是不是正确,后端有没有对应的接口

八、复习速记

  1. 核心定位:Spring 官方第二代网关,基于 Netty + WebFlux 非阻塞架构,高性能,替代 Zuul,是微服务的统一入口。
  2. 三大核心:Route 路由(转发规则)、Predicate 断言(匹配条件)、Filter 过滤器(前后置处理)。
  3. 执行流程:DispatcherHandler 分发 → 路由断言匹配 → 组装过滤器链 → 前置过滤 → Netty 转发 → 后置过滤 → 返回响应。
  4. 过滤器分类:局部过滤器单路由生效,全局过滤器全路由生效;内置负载均衡、转发等核心全局过滤器。
  5. 限流方案:内置 Redis 令牌桶限流,支持 IP、用户、路径多维度;也可整合 Sentinel 实现更丰富的流量治理。
  6. 核心红线:不能和 Spring MVC 共存;请求体只能读一次;路由按顺序匹配,精确规则放前面。
  7. 生产必做:统一鉴权、限流熔断、全局异常、跨域处理、服务发现整合、日志监控、动态路由配置。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐