Spring Cloud Gateway 深度解析:从路由原理到生产级网关实战
摘要 :Spring Cloud Gateway 作为 Spring 官方推荐的 API 网关,构建于 Project Reactor 和 Netty 之上,提供了高性能的响应式路由能力。本文从 GatewayProperties 的配置加载出发,深入解析 RouteDefinitionRouteLocator 的路由解析、FilteringWebHandler 的过滤器链编排、RoutePredicateHandlerMapping 的请求匹配机制,以及动态路由、自定义过滤器、限流熔断等生产级实战方案。
一、引言
在微服务架构中,API 网关是整个系统的统一入口。它承担着请求路由、负载均衡、认证鉴权、限流熔断、日志监控等横切关注点。Spring Cloud Gateway 取代 Netflix Zuul 成为 Spring 生态的官方网关方案,其核心优势在于:
- 响应式架构:基于 WebFlux 和 Netty,使用少量线程处理大量并发连接
- 统一过滤器链:支持全局过滤器和路由级过滤器的组合编排
- 动态路由:支持从配置中心(Nacos/Consul)实时刷新路由规则
- Spring 生态原生:与 Spring Security、Spring Cloud LoadBalancer 深度集成
二、核心架构:请求如何从网关到达下游服务
2.1 整体架构图
复制代码
Client Request
│
▼
┌─────────────────────────────────────────────────────┐
│ Netty Server (HttpServer) │
│ ReactorHttpHandlerAdapter │
└────────────────────┬────────────────────────────────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
DispatcherHandler (WebFlux 核心分发器)
│
▼
RoutePredicateHandlerMapping (网关核心 Mapping)
│
├── 1. 从 RouteLocator 获取所有 Route
│ RouteDefinitionRouteLocator
│ ↓
│ CompositeRouteLocator (组合多个源)
│ ├── PropertiesRouteDefinitionLocator (配置文件)
│ ├── NacosRouteDefinitionLocator (Nacos 配置中心)
│ └── Redis/自定义 RouteDefinitionLocator
│
├── 2. 遍历所有 Route,执行 Predicate 匹配
│ PathPredicate: /api/orders/** 匹配?
│ MethodPredicate: GET/POST 匹配?
│ HeaderPredicate: Header 条件匹配?
│
└── 3. 找到第一个匹配的 Route
↓
┌───────▼──────────────────────────────────────────┐
│ FilteringWebHandler │
│ │
│ 构建 GatewayFilterChain (Ordered 排序): │
│ ───────────────────────────────────── │
│ 0. NettyWriteResponseFilter (WRITE) │
│ 1. RouteToRequestUrlFilter (ROUTE) │
│ 2. LoadBalancerClientFilter (ROUTE) │
│ 3. WebsocketRoutingFilter (ROUTE) │
│ 4. NettyRoutingFilter (ROUTE) │
│ ───────────────────────────────────── │
│ + 路由级过滤器 (StripPrefix/Retry/...) │
│ + 自定义 GlobalFilter │
└────────────────────┬───────────────────────────────┘
│
▼
NettyRoutingFilter
│
├── WebClient HTTP 调用下游服务
│ (Netty-based, 非阻塞)
│
▼
ClientResponse 返回
│
▼
过滤器链反向执行 (Response 处理)
│
▼
NettyWriteResponseFilter
│
▼
返回 Client
2.2 三大核心组件
| 组件 |
职责 |
关键类 |
| Route |
路由定义:ID + URI + Predicate + Filter |
RouteDefinition, Route |
| Predicate |
请求匹配条件:Path/Method/Header/Query 等 |
RoutePredicateFactory |
| Filter |
请求/响应处理:修改、增强、转发 |
GatewayFilterFactory, GlobalFilter |
三、源码深度解析
3.1 路由定义加载:RouteDefinitionRouteLocator
java
复制代码
// org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator
public class RouteDefinitionRouteLocator
implements RouteLocator, ApplicationEventPublisherAware {
// ★ 组合多个 RouteDefinitionLocator
private final RouteDefinitionLocator routeDefinitionLocator;
// ★ Predicate 工厂映射
private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();
// ★ Filter 工厂映射
private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();
// ★ Gateway 配置属性
private final GatewayProperties gatewayProperties;
@Override
public Flux<Route> getRoutes() {
// ★ 核心:将 RouteDefinition 转换为 Route
return this.routeDefinitionLocator.getRouteDefinitions()
.map(this::convertToRoute)
.map(route -> {
// 打印路由日志 (DEBUG 级别)
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition matched: " + route.getId());
}
return route;
});
}
private Route convertToRoute(RouteDefinition routeDefinition) {
// 1. 组合所有 Predicate (AND 关系)
AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
// 2. 组合所有 Filter
List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
// 3. 构建 Route
return Route.async(routeDefinition)
.asyncPredicate(predicate)
.replaceFilters(gatewayFilters)
.build();
}
// ★ Predicate 组合:所有条件必须同时满足 (AND)
private AsyncPredicate<ServerWebExchange> combinePredicates(
RouteDefinition routeDefinition) {
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
// 第一个 Predicate
AsyncPredicate<ServerWebExchange> predicate = lookup(predicates.get(0));
// 后续 Predicate 用 AND 连接
for (int i = 1; i < predicates.size(); i++) {
AsyncPredicate<ServerWebExchange> andPredicate = lookup(predicates.get(i));
predicate = predicate.and(andPredicate);
}
return predicate;
}
}
3.2 请求匹配:RoutePredicateHandlerMapping
java
复制代码
// org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
private final FilteringWebHandler webHandler;
private final RouteLocator routeLocator;
@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
// ★ 将匹配到的 Route 存入 Exchange 属性
// 后续过滤器可以从 exchange.getAttribute(GATEWAY_ROUTE_ATTR) 获取
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, "Route");
// 从 RouteLocator 获取路由并匹配
return Mono.deferContextual(contextView -> lookupRoute(exchange)
.map(route -> {
// ★ 将匹配到的 Route 存入 Exchange
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, route);
// 返回 FilteringWebHandler 作为处理器
return this.webHandler;
})
.switchIfEmpty(Mono.empty()
.then(Mono.fromRunnable(() -> {
// 404: 没有匹配的路由
exchange.getAttributes()
.remove(GATEWAY_PREDICATE_ROUTE_ATTR);
if (logger.isTraceEnabled()) {
logger.trace("No RouteDefinition found for ["
+ exchange.getRequest().getURI() + "]");
}
}))));
}
// ★ 路由查找核心逻辑
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
return this.routeLocator.getRoutes()
// ★ 按 Order 排序(优先级)
.concatMap(route -> Mono.just(route)
.filterWhen(r -> {
// ★ 执行 Predicate 匹配
exchange.getAttributes()
.put(GATEWAY_PREDICATE_MATCHED_ATTR_ATTR,
r.getId());
return r.getPredicate().apply(exchange);
})
doOnNext(index -> {
exchange.getAttributes()
.put(GATEWAY_PREDICATE_ROUTE_ATTR,
route);
})
// 只要有一个匹配就停止 (take(1))
.switchIfEmpty(Mono.empty()))
.next() // 取第一个匹配的路由
.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("Route matched: " + route.getId());
}
validateRoute(route, exchange);
return route;
});
}
}
3.3 过滤器链执行:FilteringWebHandler
java
复制代码
// org.springframework.cloud.gateway.handler.FilteringWebHandler
public class FilteringWebHandler implements WebHandler {
// ★ 全局过滤器列表(自动注入所有 GlobalFilter)
private final List<GatewayFilter> globalFilters;
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
// ★ 从 Exchange 获取匹配的路由
Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
// ★ 构建过滤器链:全局过滤器 + 路由过滤器
List<GatewayFilter> gatewayFilters = route.getFilters();
List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
combined.addAll(gatewayFilters);
// ★ 按 Order 排序(AnnotationAwareOrderComparator)
AnnotationAwareOrderComparator.sort(combined);
// ★ 创建过滤器链并执行
return new DefaultGatewayFilterChain(combined).filter(exchange);
}
// ★ 默认过滤器链实现
private static class DefaultGatewayFilterChain implements GatewayFilterChain {
private final int index;
private final List<GatewayFilter> filters;
@Override
public Mono<Void> filter(ServerWebExchange exchange) {
return Mono.defer(() -> {
if (this.index < filters.size()) {
GatewayFilter filter = filters.get(this.index);
// ★ 责任链模式:递归调用下一个过滤器
DefaultGatewayFilterChain chain =
new DefaultGatewayFilterChain(this.index + 1,
this.filters);
return filter.filter(exchange, chain);
}
else {
// 所有过滤器执行完毕
return Mono.empty();
}
});
}
}
}
3.4 核心内置过滤器执行顺序
java
复制代码
// 过滤器 Order 值(数值越小越先执行 REQUEST 方向,越后执行 RESPONSE 方向)
// REQUEST 方向(正向执行):
// 0: RemoveCachedBodyFilter --- 清理缓存
// ~100: AdaptCachedBodyGlobalFilter --- 缓存 Body
// 101: NettyWriteResponseFilter --- 写响应(特殊,反向触发)
// ~1000-2000: 自定义 GlobalFilter
// 10001: RouteToRequestUrlFilter --- 解析目标 URL
// 10100: LoadBalancerClientFilter --- 负载均衡解析 lb://
// 10200: WebsocketRoutingFilter --- WebSocket 路由
// 10450: ForwardPathFilter --- forward:// 路由
// 10500: NettyRoutingFilter --- HTTP 调用下游
// 10600: ForwardRoutingFilter --- 转发到本地
// RESPONSE 方向(反向执行):
// 与 REQUEST 相反顺序,NettyWriteResponseFilter 在 RESPONSE 阶段实际执行写操作
四、路由 Predicate 深度解析
4.1 Path Predicate(最常用)
java
复制代码
// PathRoutePredicateFactory
public class PathRoutePredicateFactory
extends AbstractRoutePredicateFactory<PathConfig> {
@Override
public Predicate<ServerWebExchange> apply(PathConfig config) {
final List<PathPattern> pathPatterns = config.getPatterns().stream()
.map(this.pathPatternParser::parse)
.collect(Collectors.toList());
return exchange -> {
PathContainer path = parsePath(
exchange.getRequest().getURI().getRawPath());
// ★ 路径匹配,支持 Ant 风格 /** /{segment}
Optional<PathPattern> matched = pathPatterns.stream()
.filter(pattern -> pattern.matches(path))
.findFirst();
if (matched.isPresent()) {
// ★ 将路径变量存入 Exchange
PathPattern.PathMatchInfo matchInfo =
matched.get().matchAndExtract(path);
putUriTemplateVariables(exchange, matchInfo.getUriVariables());
return true;
}
return false;
};
}
}
// 配置示例
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/** # 匹配 /api/orders/123
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/{id} # 匹配 /api/users/456,id 存入变量
- id: generic-service
uri: lb://generic-service
predicates:
- Path=/api/{service}/** # 多级路径变量
4.2 其他常用 Predicate
| Predicate |
说明 |
示例 |
Path |
请求路径匹配 |
/api/orders/** |
Method |
HTTP 方法匹配 |
GET,POST |
Header |
请求头匹配 |
X-Api-Key, \d+ |
Query |
查询参数匹配 |
version, 2. |
Cookie |
Cookie 匹配 |
sessionId, abc.* |
Host |
Host 头匹配 |
**.example.com |
Before/After/Between |
时间窗口 |
After=2024-01-01T00:00:00+08:00 |
RemoteAddr |
IP 白名单 |
192.168.1.0/24 |
Weight |
权重分流(灰度) |
group=prod, weight=80 |
五、GatewayFilter 深度解析
5.1 请求修改型过滤器
java
复制代码
// AddRequestHeaderGatewayFilterFactory
public class AddRequestHeaderGatewayFilterFactory
extends AbstractGatewayFilterFactory<NameValueConfig> {
@Override
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
// ★ 修改请求,添加 Header
ServerHttpRequest request = exchange.getRequest().mutate()
.header(config.getName(), config.getValue())
.build();
return chain.filter(exchange.mutate().request(request).build());
};
}
}
// 等效配置
spring:
cloud:
gateway:
default-filters:
- AddRequestHeader=X-Gateway-Source, spring-cloud-gateway
- AddRequestParameter=version, 2.0
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- SetRequestHost=order-service:8080
5.2 路径重写与转发
yaml
复制代码
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1 # StripPrefix: 去掉前缀 样例: /api/orders/123 → /orders/123
- id: order-service
uri: lb://order-service
predicates:
- Path=/orders/**
filters:
- PrefixPath=/v2 # PrefixPath: 添加前缀 样例: /orders/123 → /v2/orders/123
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/**
filters:
- RewritePath=/api/(?<segment>.*), /$\\{segment} # RewritePath: 正则重写 样例: /api/orders → /orders
# SetPath: 模板路径
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/{segment}
filters:
- SetPath=/{segment} # /api/orders/123 → /orders/123
5.3 重试与熔断
yaml
复制代码
# 重试与熔断
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
# ★ 重试配置
- name: Retry
args:
retries: 3 # 最多重试 3 次
statuses: BAD_GATEWAY, GATEWAY_TIMEOUT # 对哪些状态码重试
methods: GET, POST # 对哪些方法重试
backoff:
firstBackoff: 50ms # 首次退避
maxBackoff: 500ms # 最大退避
factor: 2 # 指数退避因子
basedOnPreviousValue: false
# ★ 熔断配置(需集成 CircuitBreaker)
- name: CircuitBreaker
args:
name: orderCb
fallbackUri: forward:/fallback
statusCodes:
- 500
- "GATEWAY_TIMEOUT"
六、生产级实战:动态路由与自定义开发
6.1 基于 Nacos 的动态路由
java
复制代码
/**
* ★ Nacos 动态路由:监听配置变更,实时刷新路由
* 无需重启 Gateway,路由规则从 Nacos 配置中心获取
*/
@Configuration
public class NacosDynamicRouteService implements ApplicationEventPublisherAware {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
@Autowired
private NacosConfigManager nacosConfigManager;
private ApplicationEventPublisher publisher;
private static final String DATA_ID = "gateway-routes.json";
private static final String GROUP = "DEFAULT_GROUP";
@PostConstruct
public void init() throws NacosException {
// 1. 首次加载路由
String config = nacosConfigManager.getConfigService()
.getConfig(DATA_ID, GROUP, 5000);
loadRoutes(config);
// 2. ★ 监听 Nacos 配置变更
nacosConfigManager.getConfigService().addListener(
DATA_ID, GROUP, new Listener() {
@Override
public void receiveConfigInfo(String config) {
// 清空旧路由并加载新路由
loadRoutes(config);
}
@Override
public Executor getExecutor() {
return null; // 使用默认线程
}
}
);
}
private void loadRoutes(String config) {
try {
List<RouteDefinition> definitions =
JSON.parseArray(config, RouteDefinition.class);
for (RouteDefinition definition : definitions) {
// ★ 写入路由定义
routeDefinitionWriter.save(
Mono.just(definition)
).subscribe();
}
// ★ 发布刷新事件,Gateway 重新加载路由
this.publisher.publishEvent(
new RefreshRoutesEvent(this));
log.info("动态路由刷新完成,共 {} 条", definitions.size());
} catch (Exception e) {
log.error("动态路由加载失败", e);
}
}
@Override
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
}
// Nacos 配置格式 (gateway-routes.json):
// [
// {
// "id": "order-service",
// "predicates": [{"name": "Path", "args": {"pattern": "/api/orders/**"}}],
// "filters": [{"name": "StripPrefix", "args": {"parts": "1"}}],
// "uri": "lb://order-service"
// }
// ]
6.2 自定义全局过滤器:认证鉴权
java
复制代码
/**
* ★ 认证全局过滤器:JWT 验证 + 用户透传
* Order: HIGHEST_PRECEDENCE + 100 (确保在负载均衡之前执行)
*/
@Component
@Slf4j
public class AuthGlobalFilter implements GlobalFilter, Ordered {
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
// 1. 跳过白名单路径
if (isWhiteList(path)) {
return chain.filter(exchange);
}
// 2. 提取 Token
String token = extractToken(request);
if (token == null) {
return unauthorized(exchange, "Missing token");
}
// 3. 验证 Token (支持 Redis 黑名单检查)
return validateToken(token)
.flatMap(valid -> {
if (!valid) {
return unauthorized(exchange, "Invalid token");
}
// 4. ★ 透传用户信息到下游服务
String userId = jwtTokenProvider.getUserId(token);
String roles = jwtTokenProvider.getRoles(token);
ServerHttpRequest mutated = request.mutate()
.header("X-User-Id", userId)
.header("X-User-Roles", roles)
.header("X-Gateway-Time",
String.valueOf(System.currentTimeMillis()))
.build();
return chain.filter(
exchange.mutate().request(mutated).build());
});
}
private Mono<Boolean> validateToken(String token) {
return Mono.fromCallable(() -> {
// 检查 Redis 黑名单
Boolean blacklisted = redisTemplate.hasKey(
"token:blacklist:" + token);
if (Boolean.TRUE.equals(blacklisted)) {
return false;
}
return jwtTokenProvider.validate(token);
}).subscribeOn(Schedulers.boundedElastic());
}
private Mono<Void> unauthorized(ServerWebExchange exchange,
String message) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().setContentType(
MediaType.APPLICATION_JSON);
String body = String.format(
"{\"code\":401,\"message\":\"%s\"}", message);
DataBuffer buffer = response.bufferFactory()
.wrap(body.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer));
}
@Override
public int getOrder() {
// ★ 在 LoadBalancerClientFilter (10100) 之前执行
return Ordered.HIGHEST_PRECEDENCE + 100;
}
private boolean isWhiteList(String path) {
return path.startsWith("/auth/") ||
path.startsWith("/actuator/") ||
path.equals("/health");
}
private String extractToken(ServerHttpRequest request) {
String bearer = request.getHeaders()
.getFirst(HttpHeaders.AUTHORIZATION);
if (bearer != null && bearer.startsWith("Bearer ")) {
return bearer.substring(7);
}
// 支持从 Query Param 获取
return request.getQueryParams().getFirst("access_token");
}
}
6.3 自定义限流过滤器:Redis 滑动窗口
java
复制代码
/**
* ★ 自定义限流过滤器:基于 Redis 滑动窗口
* 支持按用户/API/IP 多维度限流
*/
@Component
public class RateLimitGatewayFilterFactory extends
AbstractGatewayFilterFactory<RateLimitGatewayFilterFactory.Config> {
@Autowired
private StringRedisTemplate redisTemplate;
public RateLimitGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
String key = resolveKey(exchange, config);
long now = System.currentTimeMillis();
long windowStart = now - config.getWindowMs();
// ★ Redis ZSet 滑动窗口实现
String script =
"redis.call('zremrangeByScore', KEYS[1], 0, ARGV[1]);" +
"local count = redis.call('zcard', KEYS[1]);" +
"if count < tonumber(ARGV[2]) then " +
" redis.call('zadd', KEYS[1], ARGV[3], ARGV[4]);" +
" redis.call('pexpire', KEYS[1], ARGV[5]);" +
" return 1;" +
"else " +
" return 0;" +
"end";
String requestId = exchange.getRequest().getId();
return Mono.fromCallable(() ->
redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Collections.singletonList(key),
String.valueOf(windowStart),
String.valueOf(config.getMaxRequests()),
String.valueOf(now),
requestId,
String.valueOf(config.getWindowMs())
)
).subscribeOn(Schedulers.boundedElastic())
.flatMap(allowed -> {
if (allowed != null && allowed == 1) {
// ★ 添加限流响应头
exchange.getResponse().getHeaders()
.set("X-RateLimit-Limit",
String.valueOf(config.getMaxRequests()));
return chain.filter(exchange);
} else {
return rateLimitExceeded(exchange);
}
});
};
}
private String resolveKey(ServerWebExchange exchange, Config config) {
String prefix = "ratelimit:" + config.getKeyPrefix() + ":";
switch (config.getKeyType()) {
case "ip":
return prefix + exchange.getRequest().getRemoteAddress()
.getAddress().getHostAddress();
case "user":
return prefix + exchange.getRequest().getHeaders()
.getFirst("X-User-Id");
case "api":
default:
return prefix + exchange.getRequest().getURI().getPath();
}
}
private Mono<Void> rateLimitExceeded(ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
String body = "{\"code\":429,\"message\":\"Rate limit exceeded\"}";
DataBuffer buffer = response.bufferFactory()
.wrap(body.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer));
}
@Data
public static class Config {
private String keyPrefix = "gateway"; // Redis key 前缀
private String keyType = "api"; // ip/user/api
private int maxRequests = 100; // 窗口内最大请求数
private long windowMs = 60000; // 窗口大小(ms)
}
}
// 使用
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- name: RateLimit
args:
keyPrefix: order_api
keyType: user
maxRequests: 10
windowMs: 60000
6.4 灰度发布:基于权重的流量分流
yaml
复制代码
spring:
cloud:
gateway:
routes:
# 生产版本 (80% 流量)
- id: order-service-prod
uri: lb://order-service
predicates:
- Path=/api/orders/**
- Weight=group, 8 # ★ 权重 80%
filters:
- StripPrefix=1
# 灰度版本 (20% 流量)
- id: order-service-canary
uri: lb://order-service-v2 # v2 版本服务
predicates:
- Path=/api/orders/**
- Weight=group, 2 # ★ 权重 20%
filters:
- StripPrefix=1
# 更精细的灰度:基于 Header/Cookie
- id: order-service-canary-header
uri: lb://order-service-v2
predicates:
- Path=/api/orders/**
- Header=X-Canary, true # ★ 带特定 Header 的请求走灰度
6.5 完整生产配置
yaml
复制代码
server:
port: 8080
spring:
application:
name: api-gateway
cloud:
gateway:
# ★ 全局默认过滤器
default-filters:
# 响应头去重
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
# 添加网关标识
- AddResponseHeader=X-Gateway-Service, ${spring.application.name}
- AddResponseHeader=X-Gateway-Version, 1.0.0
# 请求日志(开发环境开启)
# - AddRequestParameter=traceId, #{T(java.util.UUID).randomUUID()}
# ★ 全局跨域
globalcors:
cors-configurations:
'[/**]':
allowedOrigins:
- "https://app.example.com"
- "https://admin.example.com"
allowedMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
allowedHeaders: ["*"]
allowCredentials: true
maxAge: 3600
# ★ 路由配置
routes:
# 订单服务
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
- Method=GET,POST,PUT,DELETE
filters:
- StripPrefix=1
- name: Retry
args:
retries: 2
statuses: BAD_GATEWAY,SERVICE_UNAVAILABLE
methods: GET,POST
backoff:
firstBackoff: 50ms
maxBackoff: 500ms
- name: CircuitBreaker
args:
name: orderCb
fallbackUri: forward:/fallback/order
# 用户服务
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
# 认证服务(无需鉴权)
- id: auth-service
uri: lb://auth-service
predicates:
- Path=/auth/**
filters:
- StripPrefix=1
# WebSocket 路由
- id: ws-service
uri: lb:ws://notification-service
predicates:
- Path=/ws/**
# ★ HTTP 客户端配置 (WebClient)
httpclient:
connect-timeout: 5000
response-timeout: 30s
pool:
type: elastic
max-idle-time: 10s
max-life-time: 60s
ssl:
useInsecureTrustManager: false
# ★ 开启熔断 (需引入 spring-cloud-starter-circuitbreaker-reactor-resilience4j)
circuitbreaker:
resilience4j:
enabled: true
# ★ 注册中心(Nacos)
nacos:
discovery:
server-addr: nacos-server:8848
namespace: ${NACOS_NAMESPACE:production}
group: DEFAULT_GROUP
config:
server-addr: nacos-server:8848
namespace: ${NACOS_NAMESPACE:production}
group: DEFAULT_GROUP
file-extension: yaml
refresh-enabled: true
# 监控
management:
endpoints:
web:
exposure:
include: gateway,health,metrics,prometheus
endpoint:
gateway:
enabled: true
# 日志
logging:
level:
org.springframework.cloud.gateway: DEBUG
reactor.netty: INFO
七、Gateway 与 Spring Cloud LoadBalancer 集成
7.1 负载均衡解析流程
java
复制代码
// LoadBalancerClientFilter (Order: 10100)
public class ReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
// 只处理 lb:// 开头的 URI
String schemePrefix = exchange.getAttribute(
GATEWAY_SCHEME_PREFIX_ATTR);
if (url == null ||
(!"lb".equals(url.getScheme()) &&
!"lb".equals(schemePrefix))) {
return chain.filter(exchange);
}
// ★ 保留原始 URI
addOriginalRequestUrl(exchange, url);
// ★ 通过 LoadBalancer 选择实例
return choose(exchange)
.doOnNext(response -> {
// 无可用实例
if (!response.hasServer()) {
throw NotFoundException.create(
"Unable to find instance for " + url.getHost());
}
ServiceInstance instance = response.getServer();
// ★ 将 lb://order-service 替换为 http://192.168.1.10:8080
URI requestUrl = LoadBalancerUriTools.reconstructURI(
instance, url);
exchange.getAttributes().put(
GATEWAY_REQUEST_URL_ATTR, requestUrl);
})
.then(chain.filter(exchange));
}
private Mono<Response<ServiceInstance>> choose(ServerWebExchange exchange) {
URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
String serviceId = uri.getHost();
// ★ 委托给 Spring Cloud LoadBalancer
return client.choose(serviceId);
}
}
八、性能调优与监控
8.1 关键性能指标
| 指标 |
健康阈值 |
说明 |
| 吞吐量 (RPS) |
> 10,000 |
单机 Gateway 能力 |
| P99 延迟 |
< 50ms |
网关本身延迟 |
| 内存使用 |
< 70% |
堆外内存 + 堆内存 |
| 连接池利用率 |
< 80% |
下游连接池 |
| 错误率 |
< 0.1% |
5xx 比例 |
8.2 调优参数
yaml
复制代码
spring:
cloud:
gateway:
httpclient:
pool:
max-connections: 1000 # 最大连接数
max-idle-time: 30s # 空闲连接超时
eviction-interval: 60s # 驱逐检查间隔
connect-timeout: 3000 # 连接超时
response-timeout: 30s # 响应超时
wiretap: false # 调试用,生产关闭
server:
netty:
connection-timeout: 2s
idle-timeout: 60s
8.3 Prometheus 监控
yaml
复制代码
# Gateway 指标端点
# /actuator/prometheus
# 关键指标:
# - gateway_requests_seconds_count{route="order-service"}
# - gateway_requests_seconds_sum{route="order-service"}
# - resilience4j_circuitbreaker_state{name="orderCb"}
九、总结
| 维度 |
Spring Cloud Gateway |
Kong |
Nginx + Lua |
| 架构 |
WebFlux/Netty |
OpenResty |
Nginx |
| 性能 |
高 |
极高 |
极高 |
| Spring 集成 |
原生 |
需插件 |
无 |
| 动态路由 |
优秀 |
良好 |
需 Lua |
| 扩展性 |
Java 代码 |
Lua/Go |
Lua |
| 学习曲线 |
低(Spring) |
中 |
高 |
| 适用场景 |
Spring 生态 |
通用 |
高性能场景 |