背景

本文是《Java 后端从小白到大神》修仙系列之框架学习,Spring Cloud 微服务实战系列的 第三篇。Spring Cloud 是构建微服务架构的基石,拥有完整的服务治理生态,在云原生架构中广泛应用。本系列将从架构认知到实际工程,逐步构建一套企业级 Spring Cloud 微服务项目。若想详细学习请点击首篇博文开始,现在开始学习。

文章概览

Spring Cloud Gateway 网关核心能力实战:

  1. Gateway 与 Zuul 的对比与选择
  2. 静态路由 / 动态路由机制详解
  3. 自定义全局过滤器:统一 Token 校验
  4. 请求增强:请求头修改 / 日志埋点
  5. 熔断、限流、重试机制接入(结合 Resilience4j)

1. Gateway 与 Zuul 的对比与选择

Spring Cloud Gateway 是基于 WebFlux 实现的网关组件,具备非阻塞、高性能、过滤链强扩展能力等特性。它在 Spring Cloud 微服务体系中扮演统一入口、请求路由、安全控制、限流熔断等重要角色。

维度 Zuul(1.x) Spring Cloud Gateway
技术栈 基于 Servlet、阻塞 IO 基于 WebFlux、响应式、非阻塞
性能 性能瓶颈明显 高吞吐、低延迟
支持功能 路由、过滤器、基本限流 路由、过滤器、断言、限流、熔断
自定义能力 支持但扩展复杂 基于 filter 链式扩展简单强大
SpringBoot 3 不兼容 完美兼容

选择建议

Spring Boot 2.4+ / 3.x 项目统一推荐使用 Spring Cloud Gateway,Zuul 已不再维护。

2. 静态路由 / 动态路由机制详解

2.1 静态路由配置(推荐开发阶段)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/users/**
        - id: task-service
          uri: lb://task-service
          predicates:
            - Path=/tasks/**

说明:

  • lb:// 表示通过 Nacos 注册中心负载均衡调用;
  • Path 为断言(Predicate),控制路由路径;
  • 结合 StripPrefix 可去掉路径前缀。

2.2 动态路由配置(企业实战常见)

企业级中间件平台或多租户 SaaS 通常将路由配置存储在数据库 / Nacos 中,配合动态刷新:

1
2
3
4
5
6
7
spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true

自动根据服务名生成路由路径,例如访问:

http://localhost:9000/user-service/users/list

配置含义解释

配置项 含义
enabled: true 开启自动服务发现的路由机制
lower-case-service-id 将服务名转小写作为路径前缀(因为某些注册中心区分大小写)

实现原理

当你开启这项配置后:

  1. Spring Cloud Gateway 会自动订阅注册中心(比如 Nacos)中的服务列表。
  2. 对每个服务(比如 USER-SERVICE),网关会自动注册一条路由规则。
  3. 这条规则相当于:
1
2
3
4
- id: user-service
  uri: lb://user-service
  predicates:
    - Path=/user-service/**

也就是说,网关自动帮你做了这个事情,你就不需要手动去写 routes: 里面的配置。

实际访问路径怎么构成?

假设有个服务注册名叫:

user-service

当你访问:

http://localhost:9000/user-service/users/list

路由流程如下:

  1. 网关检测路径匹配 /user-service/**
  2. 自动转发到 Nacos 注册的 user-service
  3. 然后把 /users/list 交给 user-service 处理。

你不需要配置 routes,只需要:

  • 服务成功注册到 Nacos;
  • 服务有对应的 Controller 处理 /users/list
  • 开启了 discovery.locator.enabled=true

3. 自定义全局过滤器:统一 Token 校验

3.1 自定义 TokenFilter 实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Component
public class TokenFilter implements GlobalFilter, Ordered {

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    String token = exchange.getRequest().getHeaders().getFirst("Authorization");
    if (StringUtils.isEmpty(token)) {
      exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
      return exchange.getResponse().setComplete();
    }
    // 可验证 JWT 合法性
    return chain.filter(exchange);
  }

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

3.2 启用顺序说明

  • Ordered 接口可控制执行顺序;
  • 数字越小优先级越高。

4. 请求增强:请求头修改 / 日志埋点

4.1 请求头增强

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/users/**
          filters:
            - AddRequestHeader=X-Request-From, gateway
            - RemoveRequestHeader=Cookie

4.2 自定义日志埋点 Filter

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Slf4j
@Component
public class LogFilter implements GlobalFilter, Ordered {

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    String path = exchange.getRequest().getURI().getPath();
    log.info("请求路径:{}", path);
    return chain.filter(exchange);
  }

  @Override
  public int getOrder() {
    return 0;
  }
}

可用于记录 traceId、userId、链路追踪等。

5. 限流、熔断、重试机制接入(结合 Resilience4j)

5.1 限流过滤器(基于令牌桶)

1
2
3
4
5
6
filters:
  - name: RequestRateLimiter
    args:
      redis-rate-limiter.replenishRate: 5  # 每秒生成令牌数
      redis-rate-limiter.burstCapacity: 10 # 桶容量
      key-resolver: "#{@remoteAddrKeyResolver}"

配合 Redis 使用,全局限流或 IP 维度限流。

注册限流 key 解析器:

1
2
3
4
@Bean
public KeyResolver remoteAddrKeyResolver() {
    return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}

5.2 熔断器集成 Resilience4j

需引入依赖并配置:

1
2
3
4
<dependency>
  <groupId>io.github.resilience4j</groupId>
  <artifactId>resilience4j-spring-boot3</artifactId>
</dependency>

在网关中可通过 filter 进行 fallback 设置。

5.3 重试机制

1
2
3
4
5
filters:
  - name: Retry
    args:
      retries: 3
      statuses: BAD_GATEWAY, GATEWAY_TIMEOUT

适用于临时性错误重试,例如网络抖动场景。

6. 搭建第二个微服务 gateway-service

第一步:gateway-service 的 pom.xml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<project>
  <parent>
    <groupId>com.yutao</groupId>
    <artifactId>cloudtask</artifactId>
    <version>1.0.0</version>
  </parent>

  <artifactId>gateway-service</artifactId>

  <dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-gateway</artifactId>
    </dependency>

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

    <!-- Nacos Config -->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>

    <!-- Actuator 监控 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <!-- Redis + Gateway 内置限流 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    </dependency>
  </dependencies>
</project>

第二步:添加模块 gateway-service
创建 cloudtask/gateway-service 模块,并添加如下启动类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package com.yutao.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GatewayServiceApp {
    public static void main(String[] args) {
        SpringApplication.run(GatewayServiceApp.class, args);
    }
}

第三步:创建 application.yml 和 bootstrap.yml 文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
server:
  port: 9999

spring:
  application:
    name: gateway-service
  config:
    import: nacos:gateway-service-dev.yaml?namespace=47bd187a-3446-43a7-8a56-573dc8abc147&serverAddr=127.0.0.1:8848

  cloud:
    nacos:
      discovery:
        namespace: 47bd187a-3446-43a7-8a56-573dc8abc147

management:
  endpoints:
    web:
      exposure:
        include: "*"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
spring:
  application:
    name: gateway-service
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        namespace: 47bd187a-3446-43a7-8a56-573dc8abc147
        group: DEFAULT_GROUP
      discovery:
        namespace: 47bd187a-3446-43a7-8a56-573dc8abc147

第四步:Nacos 中注册的配置文件内容 gateway-service-dev.yaml

请上传至 Nacos 配置中心,DataId:gateway-service-dev.yaml,Group:DEFAULT_GROUP

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true   # 启用服务注册中心自动路由
          lower-case-service-id: true
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user/**
          filters:
            - StripPrefix=1

        - id: task-service
          uri: lb://task-service
          predicates:
            - Path=/task/**
          filters:
            - StripPrefix=1

第五步:统一鉴权过滤器:AuthGlobalFilter.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.yutao.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getHeaders().getFirst("Authorization");

        if (token == null || token.isBlank()) {
            exchange.getResponse().setStatusCode(org.springframework.http.HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }

        // 模拟校验逻辑(实际应解析 JWT)
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0; // 优先级最高
    }
}

第六步:请求头修改 / 日志埋点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.cloudtask.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 给请求添加统一头信息
 */
@Component
public class RequestHeaderEnhancer implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest().mutate()
            .header("X-Gateway-Source", "cloudtask-gateway")
            .build();
        return chain.filter(exchange.mutate().request(request).build());
    }

    @Override
    public int getOrder() {
        return 1;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.cloudtask.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 请求日志埋点
 */
@Component
public class LoggingGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();
        String ip = exchange.getRequest().getHeaders().getFirst("X-Forwarded-For");

        System.out.printf("[GatewayLog] 请求路径: %s | IP: %s%n", path, ip != null ? ip : "unknown");

        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 2;
    }
}

第七步:限流机制(Redis + Gateway 内置限流)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package com.cloudtask.gateway.config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;

/**
 * 限流使用 IP 作为 key
 */
@Configuration
public class RateLimiterConfig {

    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(
            exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
        );
    }
}

修改 application.yml,添加限流配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 5     # 每秒允许5个请求
                redis-rate-limiter.burstCapacity: 10    # 最大瞬时请求数
                key-resolver: "#{@ipKeyResolver}"       # 使用IP作为限流维度

第八步:熔断机制(Fallback) + Retry 重试机制

这部分我们不再额外引入 Hystrix(已弃用)或 Sentinel(重量级),而是直接使用 Spring Cloud Gateway 原生能力 + Resilience4j 实现轻量化熔断与重试功能。

添加依赖

1
2
3
4
5
6
7
8
9
<!-- Resilience4j 熔断与重试 -->
<dependency>
  <groupId>io.github.resilience4j</groupId>
  <artifactId>resilience4j-spring-boot3</artifactId>
</dependency>
<dependency>
  <groupId>io.github.resilience4j</groupId>
  <artifactId>resilience4j-reactor</artifactId>
</dependency>

配置 Gateway 路由中的 fallback、retry

修改 application.yml,给某个路由加上熔断与重试配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user/**
          filters:
            - StripPrefix=1
            - name: Retry
              args:
                retries: 3                     # 最大重试次数
                statuses: BAD_GATEWAY          # 针对 502 重试
                methods: GET                   # 仅 GET 方法重试
            - name: CircuitBreaker
              args:
                name: userServiceCircuit
                fallbackUri: forward:/fallback/user

添加 fallback 控制器(熔断时回退)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.cloudtask.gateway.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FallbackController {

    @GetMapping("/fallback/user")
    public String userFallback() {
        return "user-service 当前不可用,请稍后再试(熔断降级)";
    }
}

自定义 Resilience4j 熔断策略(非必须)

application.yml(高级配置):

1
2
3
4
5
6
7
resilience4j:
  circuitbreaker:
    instances:
      userServiceCircuit:
        slidingWindowSize: 10           # 滑动窗口请求数
        failureRateThreshold: 50       # 失败率超过 50% 触发熔断
        waitDurationInOpenState: 10s   # 熔断后等待时间

第九步:启动验证

  • 保证 Nacos 已运行,并存在 gateway-service-dev.yaml
  • 启动 gateway-service 服务
  • 网关会注册到 Nacos,同时将 /user/、/task/ 路由请求自动转发到目标服务
  • 请求时请携带请求头:Authorization: token123 例如:
1
curl http://localhost:9999/user/info -H "Authorization: token123"

总结

Spring Cloud Gateway 作为微服务网关,具备:

静态 / 动态路由能力 支持统一身份校验、安全过滤器 提供链式请求增强与日志埋点 易于集成限流、熔断、重试等治理能力

随着服务拆分与业务增长,统一入口治理将越来越重要,Gateway 的定制能力是现代云原生架构不可或缺的一环。