Spring Cloud Gateway 深度实战
一、为什么我们需要 API 网关?
在讲 Spring Cloud Gateway 之前,我们先搞清楚:没有网关的微服务架构,到底有哪些痛点?
1. 客户端复杂度高
如果没有网关,客户端需要直接调用各个微服务,需要维护所有服务的地址。一个系统有几十个微服务,客户端就要维护几十个地址,复杂度极高。
2. 代码冗余严重
每个微服务都要重复实现认证、授权、跨域、限流、日志、监控等横切关注点。这些功能和业务逻辑无关,但每个服务都要写一遍,代码冗余严重,维护成本极高。
3. 无法统一治理
没有统一的入口,无法对所有请求进行统一的流量控制、安全防护、日志审计。一旦出现问题,很难定位和排查。
4. 服务暴露风险高
所有微服务都直接暴露给外部,安全风险极高。攻击者可以直接攻击任何一个微服务,很容易造成系统瘫痪。
API 网关的核心价值
API 网关是微服务架构的统一入口,所有的外部请求都先经过网关,再由网关转发到对应的微服务。它主要解决以下问题:
- 统一入口:客户端只需要和网关交互,不需要知道各个微服务的地址
- 统一认证授权:所有请求都在网关层进行认证和授权,微服务不需要再实现这些功能
- 统一流量治理:限流、熔断、降级、负载均衡都在网关层实现
- 统一日志监控:所有请求的日志都在网关层统一收集和分析
- 隐藏内部服务:微服务不直接暴露给外部,提高了系统的安全性
- 协议转换:可以将 HTTP 协议转换为内部的 RPC 协议
二、Spring Cloud Gateway 是什么?
Spring Cloud Gateway 是 Spring Cloud 团队基于 Spring 5、Spring Boot 2 和 Project Reactor 开发的新一代 API 网关。它旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式。
为什么 Spring Cloud Gateway 取代了 Zuul?
Zuul 是 Netflix 开源的第一代网关,基于 Servlet 2.5,使用阻塞 IO,性能较差。而 Spring Cloud Gateway 基于非阻塞的 Reactor 框架,支持异步非阻塞,性能是 Zuul 的 2-3 倍。
两者的核心对比:
表格
| 特性 | Zuul 1.x | Spring Cloud Gateway |
|---|---|---|
| 底层框架 | Servlet | Reactor + Netty |
| IO 模型 | 阻塞 IO | 非阻塞 IO |
| 性能 | 较差 | 优秀 |
| 支持异步 | 不支持 | 支持 |
| 功能丰富度 | 一般 | 丰富 |
| 社区活跃度 | 低 | 高 |
Spring Cloud Gateway 的核心特点
- 基于 Spring Boot 2 和 Spring 5:无缝集成 Spring 生态
- 非阻塞异步:基于 Reactor 框架,支持高并发
- 强大的路由功能:支持多种路由断言和过滤器
- 集成 Spring Cloud 生态:无缝集成 Nacos、Sentinel、Hystrix 等组件
- 易于扩展:支持自定义断言和过滤器
- 支持动态路由:可以动态修改路由规则,不需要重启服务
三、Spring Cloud Gateway 核心架构与概念
Spring Cloud Gateway 的核心架构非常简单,由三个核心概念组成:路由(Route)、断言(Predicate)、过滤器(Filter)。
plaintext
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 客户端请求 │───▶│ 网关入口 │───▶│ 断言匹配 │───▶│ 过滤器链 │───▶│ 目标微服务 │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
1. 路由(Route)
路由是 Gateway 最基本的组件,它定义了请求的转发规则。一个路由由一个 ID、一个目标 URI、一组断言和一组过滤器组成。
当请求到达网关时,网关会根据断言来匹配路由,如果匹配成功,就会将请求转发到目标 URI,并经过过滤器链的处理。
2. 断言(Predicate)
断言是一个布尔表达式,用于判断请求是否符合某个条件。Spring Cloud Gateway 提供了多种内置的断言工厂,可以根据请求的路径、方法、头、参数、时间等进行匹配。
只有当所有断言都返回 true 时,请求才会匹配到对应的路由。
3. 过滤器(Filter)
过滤器用于对请求和响应进行修改和处理。Spring Cloud Gateway 提供了多种内置的过滤器工厂,可以实现添加请求头、添加响应头、重写路径、限流、熔断等功能。
过滤器分为两种:
- 局部过滤器:只作用于某个特定的路由
- 全局过滤器:作用于所有的路由
四、Spring Boot 整合 Spring Cloud Gateway
下面我就以 Spring Boot 2.7.x 和 Spring Cloud 2021.0.x 为例,教你如何快速整合 Spring Cloud Gateway。
第一步:引入依赖
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 整合 Nacos 服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
第二步:配置基础路由
在 application.yml 中配置路由规则:
yaml
spring:
application:
name: gateway-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
routes:
# 用户服务路由
- id: user-service-route
uri: lb://user-service # lb:// 表示使用负载均衡
predicates:
- Path=/user/** # 路径断言:匹配所有 /user 开头的请求
filters:
- StripPrefix=1 # 去掉路径的第一个前缀,将 /user/xxx 转为 /xxx
# 订单服务路由
- id: order-service-route
uri: lb://order-service
predicates:
- Path=/order/**
filters:
- StripPrefix=1
这个配置的意思是:
- 所有
/user/**开头的请求,都会被转发到user-service服务 - 所有
/order/**开头的请求,都会被转发到order-service服务 StripPrefix=1表示去掉路径的第一个前缀,比如/user/get/1会被转为/get/1再转发到user-service
现在,你只需要启动网关服务,就可以通过网关访问各个微服务了。比如访问 http://localhost:8080/user/get/1,网关会自动转发到 user-service 的 /get/1 接口。
五、核心功能详解
5.1 常用断言工厂
Spring Cloud Gateway 提供了非常丰富的断言工厂,下面是最常用的几个:
1. 路径断言(Path)
最常用的断言,根据请求路径进行匹配。
yaml
predicates:
- Path=/user/**,/admin/** # 匹配多个路径
2. 方法断言(Method)
根据请求方法进行匹配。
yaml
predicates:
- Method=GET,POST # 只匹配 GET 和 POST 请求
3. 请求头断言(Header)
根据请求头进行匹配。
yaml
predicates:
- Header=token,^[a-zA-Z0-9]+$ # 匹配 token 请求头,值符合正则表达式
4. 请求参数断言(Query)
根据请求参数进行匹配。
yaml
predicates:
- Query=name,zhangsan # 匹配 name 参数等于 zhangsan 的请求
5. 时间断言(Before/After/Between)
根据时间进行匹配。
yaml
predicates:
- After=2024-01-01T00:00:00+08:00[Asia/Shanghai] # 匹配 2024 年 1 月 1 日之后的请求
6. 组合断言
多个断言可以组合使用,只有当所有断言都满足时,才会匹配路由。
yaml
predicates:
- Path=/user/**
- Method=GET
- After=2024-01-01T00:00:00+08:00[Asia/Shanghai]
5.2 常用过滤器工厂
Spring Cloud Gateway 提供了几十种内置的过滤器工厂,下面是最常用的几个:
1. StripPrefix
去掉路径的前缀,最常用的过滤器。
yaml
filters:
- StripPrefix=1 # 去掉第一个前缀
- StripPrefix=2 # 去掉前两个前缀
2. AddRequestHeader
添加请求头。
yaml
filters:
- AddRequestHeader=X-Request-Id,{requestId} # 添加请求头 X-Request-Id
3. AddResponseHeader
添加响应头。
yaml
filters:
- AddResponseHeader=X-Response-Time,{responseTime} # 添加响应头 X-Response-Time
4. RewritePath
重写请求路径。
yaml
filters:
- RewritePath=/api/(?<segment>.*),/$\{segment} # 将 /api/xxx 转为 /xxx
5. PrefixPath
添加路径前缀。
yaml
filters:
- PrefixPath=/api # 给所有请求添加 /api 前缀
6. RequestRateLimiter
限流过滤器,基于 Redis 实现的令牌桶算法。
yaml
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10 # 令牌桶每秒填充速率
redis-rate-limiter.burstCapacity: 20 # 令牌桶最大容量
key-resolver: "#{@userKeyResolver}" # 限流键解析器
5.3 全局过滤器
全局过滤器作用于所有的路由,不需要在每个路由上单独配置。Spring Cloud Gateway 内置了很多全局过滤器,比如负载均衡过滤器、路由转发过滤器等。
我们也可以自定义全局过滤器,实现统一的日志记录、认证授权等功能。
自定义全局日志过滤器示例:
java
运行
@Component
@Slf4j
public class GlobalLogFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 记录请求开始时间
long startTime = System.currentTimeMillis();
// 获取请求信息
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().toString();
String method = request.getMethod().name();
String ip = request.getRemoteAddress().getAddress().getHostAddress();
log.info("请求开始:{} {} {}", method, path, ip);
// 执行过滤器链
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// 记录响应信息
long cost = System.currentTimeMillis() - startTime;
ServerHttpResponse response = exchange.getResponse();
int statusCode = response.getStatusCode().value();
log.info("请求结束:{} {} {} 耗时:{}ms", method, path, statusCode, cost);
}));
}
@Override
public int getOrder() {
// 过滤器执行顺序,值越小越先执行
return -1;
}
}
5.4 自定义过滤器
除了全局过滤器,我们还可以自定义局部过滤器,只作用于特定的路由。
自定义认证过滤器示例:
java
运行
@Component
public class AuthFilter implements GatewayFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取 token
String token = exchange.getRequest().getHeaders().getFirst("token");
// 验证 token
if (StringUtils.isEmpty(token) || !validateToken(token)) {
// 认证失败,返回 401
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 认证成功,继续执行
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
private boolean validateToken(String token) {
// 实际项目中这里应该调用认证服务验证 token
return "valid_token".equals(token);
}
}
然后在路由配置中使用这个过滤器:
yaml
routes:
- id: user-service-route
uri: lb://user-service
predicates:
- Path=/user/**
filters:
- StripPrefix=1
- name: AuthFilter # 使用自定义认证过滤器
六、高级特性:网关限流与熔断降级
6.1 网关限流
限流是网关最重要的功能之一,它可以防止系统被突发流量打垮。Spring Cloud Gateway 内置了基于 Redis 的限流过滤器,使用令牌桶算法实现。
第一步:引入 Redis 依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
第二步:配置 Redis
yaml
spring:
redis:
host: localhost
port: 6379
password: 123456
第三步:定义限流键解析器
java
运行
@Configuration
public class RateLimiterConfig {
@Bean
public KeyResolver userKeyResolver() {
// 根据用户 ID 限流
return exchange -> Mono.just(
Optional.ofNullable(exchange.getRequest().getHeaders().getFirst("userId"))
.orElse("anonymous")
);
}
@Bean
public KeyResolver ipKeyResolver() {
// 根据 IP 限流
return exchange -> Mono.just(
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
);
}
}
第四步:在路由中配置限流
yaml
routes:
- id: user-service-route
uri: lb://user-service
predicates:
- Path=/user/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10 # 每秒生成 10 个令牌
redis-rate-limiter.burstCapacity: 20 # 令牌桶最大容量 20
key-resolver: "#{@ipKeyResolver}" # 根据 IP 限流
6.2 熔断降级
当某个微服务出现故障时,网关可以直接熔断该服务的请求,返回降级结果,避免故障扩散到整个系统。Spring Cloud Gateway 可以很方便地整合 Sentinel 实现熔断降级。
第一步:引入 Sentinel 依赖
xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
第二步:配置 Sentinel
yaml
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080 # Sentinel 控制台地址
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: gateway-sentinel-rules
groupId: DEFAULT_GROUP
rule-type: flow
第三步:在 Sentinel 控制台配置熔断规则登录 Sentinel 控制台,就可以为各个路由配置流量控制、熔断降级规则了。
七、生产环境常见坑与最佳实践
坑 1:跨域问题
问题描述:前端调用网关接口时出现跨域错误。
解决方案:在网关层统一配置跨域:
yaml
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowed-origin-patterns: "*"
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
max-age: 3600
坑 2:路由匹配顺序问题
问题描述:多个路由的路径有重叠,导致请求匹配到了错误的路由。
解决方案:将更具体的路由放在前面,更通用的路由放在后面。Spring Cloud Gateway 会按照路由配置的顺序依次匹配。
坑 3:性能问题
问题描述:网关性能差,响应时间长。
解决方案:
- 避免在过滤器中执行耗时操作
- 合理配置线程池参数
- 开启网关缓存
- 使用集群部署,提高并发能力
坑 4:网关单点故障
问题描述:网关是系统的统一入口,一旦网关挂了,整个系统都无法访问。
解决方案:部署多个网关实例,前面用 Nginx 做负载均衡,实现网关的高可用。
最佳实践
- 统一入口:所有外部请求都经过网关,微服务不直接暴露给外部
- 统一认证授权:所有认证授权都在网关层实现,微服务不需要关心
- 统一流量治理:限流、熔断、降级都在网关层实现
- 统一日志监控:所有请求的日志都在网关层统一收集
- 合理拆分路由:按照业务模块拆分路由,便于管理
- 避免过重的网关:网关只做横切关注点,不要在网关中实现业务逻辑
- 集群部署:生产环境必须部署多个网关实例,保证高可用
- 监控网关状态:监控网关的 QPS、响应时间、错误率等指标
总结
Spring Cloud Gateway 是微服务架构中不可或缺的核心组件,它是所有流量的必经之地,负责统一入口、统一认证、统一流量治理等所有横切关注点。
- API 网关解决了微服务架构的诸多痛点,是微服务架构的必备组件
- Spring Cloud Gateway 基于非阻塞的 Reactor 框架,性能优秀,是新一代网关的首选
- 核心概念包括路由、断言和过滤器,掌握这三个概念就掌握了 Gateway 的核心
- 内置了丰富的断言和过滤器工厂,可以满足绝大多数业务需求
- 支持自定义全局过滤器和局部过滤器,易于扩展
- 可以很方便地实现限流、熔断降级等高级功能
- 生产环境要注意跨域、路由顺序、性能和高可用等问题
更多推荐




所有评论(0)