第 222 章:Spring Cloud Gateway 网关
学习目标
- 理解 API 网关职责
- 掌握 Gateway 路由配置
- 学会自定义过滤器
- 实现限流、熔断、鉴权
一、API 网关定位
1.1 网关职责
| 职责 | 说明 |
|---|---|
| 统一入口 | 对外暴露一个 IP / 域名 |
| 路由 | 根据规则转发到对应服务 |
| 鉴权 | JWT / OAuth2 校验 |
| 限流 | QPS / IP 维度 |
| 熔断 | 隔离下游故障 |
| 日志 | 统一请求日志 |
| 协议转换 | HTTP → gRPC 等 |
| 灰度 | 基于流量分发 |
1.2 网关对比
| 网关 | 语言 | 性能 | 特性 |
|---|---|---|---|
| Spring Cloud Gateway | Java | 中 | 响应式、易扩展 |
| Zuul 1.x | Java | 低 | Servlet,阻塞 |
| Kong | Lua/Nginx | 高 | 插件丰富 |
| APISIX | Lua/Nginx | 高 | 云原生 |
| Envoy | C++ | 极高 | Service Mesh 标配 |
| Traefik | Go | 高 | 自动证书 |
二、Spring Cloud Gateway 基础
2.1 依赖
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>2.2 路由配置
application.yml:
yaml
server:
port: 9000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
discovery:
locator:
enabled: true # 自动根据服务名路由
routes:
- id: order-service
uri: lb://order-service # lb = 负载均衡
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1 # 去掉 /api 前缀
- id: payment-service
uri: lb://payment-service
predicates:
- Path=/api/payments/**
- Method=POST
filters:
- StripPrefix=1
- AddRequestHeader=X-Source, gateway
- id: api-prefix
uri: lb://default-service
predicates:
- Path=/api/**2.3 三大要素
| 要素 | 作用 |
|---|---|
| Route | 由 ID、URI、Predicate、Filter 组成 |
| Predicate | 匹配条件,符合才路由 |
| Filter | 在请求前后做处理(改写、鉴权、限流) |
三、Predicate 断言
3.1 内置断言
yaml
predicates:
# 路径匹配
- Path=/api/orders/**
# 方法
- Method=GET,POST
# Header
- Header=X-Token, *
# Cookie
- Cookie=session, [a-z0-9]+
# 查询参数
- Query=name, .*
# 时间
- After=2026-01-01T00:00:00+08:00
- Before=2027-01-01T00:00:00+08:00
# Host
- Host=**.example.com
# 权重
- Weight=group1, 83.2 自定义 Predicate
java
@Component
public class AuthTokenPredicate
extends AbstractRoutePredicateFactory<AuthTokenPredicate.Config> {
public AuthTokenPredicate() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
String token = exchange.getRequest()
.getHeaders().getFirst("Authorization");
return token != null && token.startsWith("Bearer ");
};
}
@Data
public static class Config {
private String tokenName;
}
}使用:
yaml
predicates:
- name: AuthToken
args:
tokenName: Authorization四、Filter 过滤器
4.1 内置 Filter
| Filter | 说明 |
|---|---|
StripPrefix | 去掉前 N 段路径 |
PrefixPath | 添加前缀 |
AddRequestHeader | 添加请求头 |
RemoveRequestHeader | 删除请求头 |
AddResponseHeader | 添加响应头 |
Hystrix/Sentinel | 熔断降级 |
RequestRateLimiter | 限流 |
RedirectTo | 重定向 |
yaml
filters:
- StripPrefix=1
- AddRequestHeader=X-Source, gateway
- AddResponseHeader=X-Response-Time, 100
- RequestRateLimiter=10, 20, 100, user # replenishRate=10, burstCapacity=20, 60s4.2 GlobalFilter 全局过滤
java
@Component
public class LoggingGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest req = exchange.getRequest();
log.info("[{}] {} from {}",
req.getId(), req.getURI(), req.getRemoteAddress());
long start = System.currentTimeMillis();
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
long cost = System.currentTimeMillis() - start;
log.info("[{}] response cost={}ms", req.getId(), cost);
}));
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}4.3 GatewayFilter 局部过滤
java
@Component
public class AuthGatewayFilterFactory
extends AbstractGatewayFilterFactory<AuthGatewayFilterFactory.Config> {
@Autowired
private JwtUtil jwtUtil;
public AuthGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
String token = exchange.getRequest().getHeaders().getFirst(config.getTokenHeader());
if (token == null || !jwtUtil.validate(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 透传用户上下文
String userId = jwtUtil.getUserId(token);
exchange.getRequest().mutate()
.header("X-User-Id", userId)
.build();
return chain.filter(exchange);
};
}
@Data
public static class Config {
private String tokenHeader = "Authorization";
}
}使用:
yaml
filters:
- name: Auth
args:
tokenHeader: Authorization4.4 自定义 Filter 顺序
java
@Component
public class MyFilter implements GlobalFilter {
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 100; // 越小越早执行
}
}五、限流
5.1 内置令牌桶
yaml
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@ipKeyResolver}"java
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
);
}5.2 Sentinel 网关限流
xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>yaml
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080Dashboard 配置流控规则。
六、熔断降级
6.1 Sentinel 熔断
yaml
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080
spring.cloud.gateway.routes:
- id: order-service
uri: lb://order-service
filters:
- name: SentinelGatewayFilter6.2 自定义降级
java
@Component
public class FallbackHandler implements ErrorWebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
DataBuffer buffer = exchange.getResponse()
.bufferFactory()
.wrap("服务暂时不可用".getBytes(StandardCharsets.UTF_8));
exchange.getResponse().setStatusCode(HttpStatus.SERVICE_UNAVAILABLE);
return exchange.getResponse().writeWith(Mono.just(buffer));
}
}七、CORS 跨域
java
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}八、灰度发布
8.1 基于 Header 路由
yaml
spring.cloud.gateway.routes:
- id: order-service-v1
uri: lb://order-service
predicates:
- Path=/api/orders/**
- Header=X-Gray, v1
- id: order-service-v2
uri: lb://order-service-v2
predicates:
- Path=/api/orders/**
- Header=X-Gray, v28.2 基于权重
yaml
spring.cloud.gateway.routes:
- id: order-v1
uri: lb://order-service
predicates:
- Path=/api/orders/**
- Weight=order-group, 90
- id: order-v2
uri: lb://order-service-v2
predicates:
- Path=/api/orders/**
- Weight=order-group, 108.3 自定义灰度(基于 cookie)
java
public class GrayRoutePredicateFactory
extends AbstractRoutePredicateFactory<GrayRoutePredicateFactory.Config> {
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
HttpCookie cookie = exchange.getRequest()
.getCookies()
.getFirst(config.getCookieName());
return cookie != null && config.getVersion().equals(cookie.getValue());
};
}
}九、性能调优
9.1 Reactor Netty 参数
yaml
spring:
cloud:
gateway:
httpclient:
pool:
type: elastic
max-idle-time: 15s
response-timeout: 30s
connect-timeout: 5s
max-initial-line-length: 40969.2 关闭多余 Filter
yaml
spring:
cloud:
gateway:
default-filters: [] # 移除全局默认9.3 Prometheus 监控
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>yaml
management:
endpoints:
web:
exposure:
include: "*"
metrics:
tags:
application: api-gateway查看:http://gateway:9000/actuator/prometheus
十、统一异常处理
java
@JsonDeserialize
public class ErrorResponse {
private int code;
private String message;
private String path;
private Instant timestamp = Instant.now();
}
@Component
public class GlobalErrorHandler implements ErrorWebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ErrorResponse err;
if (ex instanceof ResponseStatusException rse) {
err = new ErrorResponse(rse.getStatusCode().value(), rse.getReason(),
exchange.getRequest().getPath().value());
exchange.getResponse().setStatusCode(rse.getStatusCode());
} else {
err = new ErrorResponse(500, "Internal Server Error",
exchange.getRequest().getPath().value());
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
}
DataBuffer buffer = exchange.getResponse().bufferFactory()
.wrap(objectMapper.writeValueAsBytes(err));
return exchange.getResponse().writeWith(Mono.just(buffer));
}
}十一、生产配置示例
yaml
server:
port: 9000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: nacos:8848
gateway:
discovery:
locator:
enabled: false # 关闭自动,手动路由
routes:
# 订单服务
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 100
redis-rate-limiter.burstCapacity: 200
key-resolver: "#{@userKeyResolver}"
# 库存服务
- id: inventory-service
uri: lb://inventory-service
predicates:
- Path=/api/inventory/**
filters:
- StripPrefix=1
# 支付服务
- id: payment-service
uri: lb://payment-service
predicates:
- Path=/api/payments/**
- Method=POST,GET
filters:
- StripPrefix=1
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Origin, RETAIN_UNIQUE
management:
endpoints:
web:
exposure:
include: health,info,gateway,prometheus十二、本章小结
| 主题 | 要点 |
|---|---|
| 路由 | ID + URI + Predicate |
| Predicate | 路径/方法/Header 匹配 |
| Filter | 改写/鉴权/限流/熔断 |
| 限流 | RequestRateLimiter / Sentinel |
| 熔断 | Sentinel Gateway Filter |
| 灰度 | 权重 / Header / Cookie |
| 监控 | Prometheus 指标 |
动手练习
- 搭建 Gateway,实现路径路由 + 鉴权
- 自定义 GlobalFilter,添加请求 ID 和慢日志
- 配置 Sentinel 网关限流,验证令牌桶行为
- 实现基于 Header 的灰度发布(80%/20%)