背景
本文是《Java 后端从小白到大神》修仙系列之框架学习,Spring Cloud 微服务实战系列的 第三篇
。Spring Cloud 是构建微服务架构的基石,拥有完整的服务治理生态,在云原生架构中广泛应用。本系列将从架构认知到实际工程,逐步构建一套企业级 Spring Cloud 微服务项目。若想详细学习请点击首篇博文开始,现在开始学习。
文章概览
Spring Cloud Gateway 网关核心能力实战:
- Gateway 与 Zuul 的对比与选择
- 静态路由 / 动态路由机制详解
- 自定义全局过滤器:统一 Token 校验
- 请求增强:请求头修改 / 日志埋点
- 熔断、限流、重试机制接入(结合 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 |
将服务名转小写作为路径前缀(因为某些注册中心区分大小写) |
实现原理
当你开启这项配置后:
- Spring Cloud Gateway 会自动订阅注册中心(比如 Nacos)中的服务列表。
- 对每个服务(比如
USER-SERVICE
),网关会自动注册一条路由规则。
- 这条规则相当于:
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
路由流程如下:
- 网关检测路径匹配
/user-service/**
;
- 自动转发到 Nacos 注册的
user-service
;
- 然后把
/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 的定制能力是现代云原生架构不可或缺的一环。