深入浅出Spring Cloud Gateway:从理论到企业级实践(二)
摘要
本文基于神话公司B2C电商平台的API网关建设经验,深入浅出地介绍Spring Cloud Gateway从理论到企业级实践的全过程。文章涵盖API网关的核心概念、主流网关框架对比、Spring Cloud Gateway技术深度解析,以及生产级网关的架构设计与最佳实践。通过真实的代码实现和架构设计,帮助读者全面理解并掌握企业级API网关的构建方法。
关键词:Spring Cloud Gateway、微服务网关、动态路由、限流熔断、控制面数据面分离
目录
第三部分:Spring Cloud Gateway深入解析
经过第二部分的对比分析,我们选择了Spring Cloud Gateway作为神话公司的网关方案。本部分将深入剖析Spring Cloud Gateway的核心原理、技术架构和实现细节,帮助读者从理论层面深入理解其工作机制。
3.1 核心概念:Route、Predicate、Filter
Spring Cloud Gateway的架构基于三个核心概念:Route(路由)、Predicate(断言)、Filter(过滤器)。理解这三者的关系,是掌握Gateway的基础。
3.1.1 Route(路由)
Route是Gateway的基本构建块,它包含以下要素:
public class Route implements Ordered {
private final String id; // 路由唯一标识
private final URI uri; // 目标URI(lb://service-name 或 http://host:port)
private final int order; // 路由优先级(数字越小优先级越高)
private final AsyncPredicate<ServerWebExchange> predicate; // 断言组合
private final List<GatewayFilter> gatewayFilters; // 过滤器列表
private final Map<String, Object> metadata; // 元数据
}
路由配置示例:
spring:
cloud:
gateway:
routes:
- id: user-service-route # 路由ID
uri: lb://user-service # 负载均衡到user-service
order: 1 # 优先级
predicates:
- Path=/api/user/** # 路径断言
filters:
- StripPrefix=2 # 剥离前缀
metadata:
response-timeout: 5000 # 元数据
3.1.2 Predicate(断言)
Predicate是Java 8函数式接口Predicate<ServerWebExchange>的实现,用于判断请求是否匹配路由规则。Gateway内置了11种常用Predicate:
| Predicate类型 | 说明 | 配置示例 | 匹配示例 |
|---|---|---|---|
| Path | 路径匹配 | Path=/api/user/** |
/api/user/123 |
| Method | HTTP方法 | Method=GET,POST |
GET /api/user |
| Header | 请求头 | Header=X-Request-Id, \d+ |
X-Request-Id: 123 |
| Query | 查询参数 | Query=token, \w+ |
?token=abc |
| Cookie | Cookie | Cookie=session, \w+ |
Cookie: session=xyz |
| Host | 主机名 | Host=**.mythos.com |
api.mythos.com |
| RemoteAddr | 远程IP | RemoteAddr=192.168.1.0/24 |
192.168.1.10 |
| Weight | 权重路由 | Weight=group1, 8 |
80%流量 |
| After | 时间之后 | After=2025-01-01T00:00:00+08:00 |
2025年后生效 |
| Before | 时间之前 | Before=2025-12-31T23:59:59+08:00 |
2025年前生效 |
| Between | 时间区间 | Between=... |
特定时间段 |
Predicate组合(AND逻辑):
predicates:
- Path=/api/user/**
- Method=GET,POST
- Header=X-Request-Source, mobile
# 同时满足上述三个条件才匹配
自定义Predicate示例:
@Component
public class UserTypeRoutePredicateFactory
extends AbstractRoutePredicateFactory<UserTypeRoutePredicateFactory.Config> {
public UserTypeRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
String userType = exchange.getRequest()
.getHeaders()
.getFirst("X-User-Type");
return config.getUserType().equals(userType);
};
}
public static class Config {
private String userType;
// getter/setter
}
}
predicates:
- UserType=CUSTOMER # 使用自定义Predicate
3.1.3 Filter(过滤器)
Filter是Gateway的核心功能实现机制,分为GatewayFilter(路由级)和GlobalFilter(全局级)。
Filter执行顺序:
内置GatewayFilter(30+种):
| Filter类型 | 功能 | 配置示例 |
|---|---|---|
| AddRequestHeader | 添加请求头 | AddRequestHeader=X-Gateway-Id, gateway-1 |
| AddRequestParameter | 添加请求参数 | AddRequestParameter=source, gateway |
| AddResponseHeader | 添加响应头 | AddResponseHeader=X-Response-Time, ${responseTime} |
| StripPrefix | 剥离路径前缀 | StripPrefix=2 (去掉前2段路径) |
| PrefixPath | 添加路径前缀 | PrefixPath=/v1 |
| RewritePath | 重写路径 | RewritePath=/api/(?<segment>.*), /$\{segment} |
| SetPath | 设置路径 | SetPath=/new/path |
| SetStatus | 设置响应状态码 | SetStatus=200 |
| RedirectTo | 重定向 | RedirectTo=302, https://new.url |
| RequestRateLimiter | 限流 | RequestRateLimiter=... |
| CircuitBreaker | 熔断 | CircuitBreaker=... |
| Retry | 重试 | Retry=3 |
| RequestSize | 请求大小限制 | RequestSize=5MB |
| ModifyRequestBody | 修改请求体 | 编程式配置 |
| ModifyResponseBody | 修改响应体 | 编程式配置 |
自定义GlobalFilter示例(TraceId过滤器):
@Component
@Order(-1000) // 最高优先级
public class TraceIdFilter implements GlobalFilter, Ordered {
private static final String TRACE_ID = "traceId";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 前置处理:生成TraceId
String traceId = generateTraceId();
MDC.put(TRACE_ID, traceId);
// 添加到请求头传递给下游服务
ServerHttpRequest request = exchange.getRequest().mutate()
.header("X-Trace-Id", traceId)
.build();
// 添加到响应头返回给客户端
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().add("X-Trace-Id", traceId);
// 链式调用
return chain.filter(exchange.mutate().request(request).build())
.doFinally(signalType -> MDC.remove(TRACE_ID)); // 后置处理:清理MDC
}
@Override
public int getOrder() {
return -1000;
}
private String generateTraceId() {
return "trace-" + System.currentTimeMillis() + "-"
+ ThreadLocalRandom.current().nextInt(10000);
}
}
3.2 响应式编程模型
Spring Cloud Gateway基于Project Reactor实现响应式编程,这是其高并发性能的核心原因。
3.2.1 为什么选择响应式?
传统阻塞模型(Servlet):
请求 → 线程1 → 调用服务(阻塞等待)→ 返回响应 → 线程1释放
请求 → 线程2 → 调用服务(阻塞等待)→ 返回响应 → 线程2释放
请求 → 线程3 → 调用服务(阻塞等待)→ 返回响应 → 线程3释放
问题:
- 线程与请求1:1绑定,线程资源宝贵(每个线程约1MB栈内存)
- I/O等待时线程阻塞,CPU利用率低
- 高并发场景需要大量线程(如10万QPS需要1000+线程)
响应式非阻塞模型(Reactor):
请求1 → EventLoop线程 → 发起I/O(立即返回)
请求2 → EventLoop线程 → 发起I/O(立即返回) } 少量线程
请求3 → EventLoop线程 → 发起I/O(立即返回)
...
I/O完成 → 回调处理 → 响应返回
优势:
- 少量线程处理大量并发(8核CPU通常8-16个EventLoop线程)
- 线程不阻塞,CPU利用率高
- 内存占用低,可支持10万+ QPS
3.2.2 Reactor核心概念
Mono vs Flux:
// Mono:0或1个元素的异步序列
Mono<String> mono = Mono.just("Hello");
Mono<User> user = userRepository.findById(123);
// Flux:0到N个元素的异步序列
Flux<String> flux = Flux.just("A", "B", "C");
Flux<Order> orders = orderRepository.findByUserId(456);
操作符链式调用:
public Mono<ServerResponse> handleRequest(ServerRequest request) {
return request.bodyToMono(CreateUserRequest.class) // 1. 解析请求体
.flatMap(this::validateUser) // 2. 校验用户
.flatMap(userRepository::save) // 3. 保存数据库
.map(this::toUserVO) // 4. 转换为VO
.flatMap(vo -> ServerResponse.ok() // 5. 构建响应
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(Response.success(vo)))
.onErrorResume(this::handleError); // 6. 异常处理
}
背压(Backpressure)机制:
Flux.range(1, 1000)
.onBackpressureBuffer(100) // 缓冲区100个元素
.subscribe(new Subscriber<Integer>() {
private Subscription subscription;
@Override
public void onSubscribe(Subscription s) {
this.subscription = s;
s.request(10); // 请求10个元素
}
@Override
public void onNext(Integer integer) {
// 处理元素
processElement(integer);
subscription.request(1); // 处理完请求下一个
}
});
3.2.3 Gateway中的响应式实践
典型Filter响应式写法:
@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {
@Autowired
private AuthService authService;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = extractToken(exchange.getRequest());
if (StringUtils.isEmpty(token)) {
return unauthorizedResponse(exchange); // 返回Mono<Void>
}
// 异步验证Token(不阻塞线程)
return authService.validateToken(token) // 返回 Mono<User>
.flatMap(user -> {
// Token有效,将用户信息放入上下文
exchange.getAttributes().put("userId", user.getId());
exchange.getAttributes().put("userType", user.getUserType());
return chain.filter(exchange); // 继续过滤器链
})
.onErrorResume(ex -> {
// Token无效,返回403
log.warn("Token validation failed", ex);
return forbiddenResponse(exchange);
});
}
@Override
public int getOrder() {
return -998;
}
private Mono<Void> unauthorizedResponse(ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
String body = "{\"code\":\"401\",\"message\":\"Unauthorized\"}";
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes());
return response.writeWith(Mono.just(buffer));
}
}
3.3 请求处理流程详解
理解Gateway的完整请求处理流程,对于调试和优化至关重要。
3.3.1 核心处理流程
3.3.2 核心组件解析
1. DispatcherHandler(调度器)
Spring WebFlux的核心处理器,类似Spring MVC的DispatcherServlet。
public class DispatcherHandler implements WebHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
return this.handlerMappings.get()
.concatMap(mapping -> mapping.getHandler(exchange)) // 查找Handler
.next()
.switchIfEmpty(createNotFoundError())
.flatMap(handler -> invokeHandler(exchange, handler)) // 执行Handler
.flatMap(result -> handleResult(exchange, result));
}
}
2. RoutePredicateHandlerMapping(路由映射器)
根据Predicate找到匹配的Route。
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getClass().getSimpleName());
return lookupRoute(exchange) // 查找路由
.flatMap((Function<Route, Mono<?>>) r -> {
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r); // 保存路由
return Mono.just(new FilteringWebHandler(r.getFilters())); // 返回Handler
})
.switchIfEmpty(Mono.empty());
}
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
return this.routeLocator.getRoutes()
.concatMap(route -> Mono.just(route)
.filterWhen(r -> r.getPredicate().apply(exchange)) // 断言匹配
.doOnError(e -> logger.error("Error applying predicate for route: " + route.getId(), e))
.onErrorResume(e -> Mono.empty())
)
.next(); // 返回第一个匹配的路由
}
}
3. FilteringWebHandler(过滤器执行器)
组装全局过滤器和路由过滤器,按Order排序执行。
public class FilteringWebHandler implements WebHandler {
private final List<GatewayFilter> globalFilters;
@Override
public Mono<Void> handle(ServerWebExchange 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.sort(combined);
// 构建过滤器链
return new DefaultGatewayFilterChain(combined).filter(exchange);
}
}
4. NettyRoutingFilter(路由转发过滤器)
负责实际的HTTP请求转发(Order=10000,最后执行)。
public class NettyRoutingFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
.doOnConnected(conn -> {
conn.addHandlerLast(new ReadTimeoutHandler(10, TimeUnit.SECONDS));
});
return httpClient
.headers(headers -> {
// 复制请求头
exchange.getRequest().getHeaders().forEach(headers::set);
})
.request(HttpMethod.valueOf(exchange.getRequest().getMethodValue()))
.uri(requestUrl)
.send((req, out) -> out.send(exchange.getRequest().getBody())) // 发送请求体
.response((res, body) -> {
// 处理响应
exchange.getResponse().setStatusCode(HttpStatusCode.valueOf(res.status().code()));
exchange.getResponse().getHeaders().putAll(res.responseHeaders());
return exchange.getResponse().writeWith(body);
});
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE; // 10000
}
}
3.4 内置Predicate和Filter详解
3.4.1 常用Predicate组合场景
场景1:移动端专属路由
spring:
cloud:
gateway:
routes:
- id: mobile-user-route
uri: lb://mobile-user-service
predicates:
- Path=/api/user/**
- Header=X-Client-Type, mobile # 必须是移动端
- Method=GET,POST
filters:
- AddRequestHeader=X-Platform, mobile
场景2:灰度发布(按用户ID)
routes:
# 新版本路由(用户ID尾号0-2)
- id: user-service-v2-canary
uri: lb://user-service-v2
order: 1 # 优先级高
predicates:
- Path=/api/user/**
- Header=X-User-Id, .*[012]$ # 正则匹配尾号
filters:
- AddResponseHeader=X-Version, v2
# 旧版本路由(其他用户)
- id: user-service-v1-stable
uri: lb://user-service-v1
order: 2
predicates:
- Path=/api/user/**
filters:
- AddResponseHeader=X-Version, v1
场景3:按时间段路由(营销活动)
routes:
# 活动期间路由到专用服务
- id: promotion-route
uri: lb://promotion-service
predicates:
- Path=/api/promotion/**
- Between=2025-11-11T00:00:00+08:00, 2025-11-11T23:59:59+08:00 # 双11当天
filters:
- CircuitBreaker=promotionCB
3.4.2 高级Filter实战
1. 限流Filter(Redis令牌桶)
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10 # 每秒生成10个令牌
redis-rate-limiter.burstCapacity: 20 # 桶容量20个令牌
redis-rate-limiter.requestedTokens: 1 # 每次请求消耗1个令牌
key-resolver: "#{@userKeyResolver}" # Bean名称
@Bean
public KeyResolver userKeyResolver() {
return exchange -> {
String userId = exchange.getAttribute("userId");
return Mono.justOrEmpty(userId); // 按用户ID限流
};
}
2. 熔断Filter(Resilience4j)
filters:
- name: CircuitBreaker
args:
name: userServiceCB
fallbackUri: forward:/fallback/user # 熔断降级URI
@Bean
public CircuitBreakerConfig circuitBreakerConfig() {
return CircuitBreakerConfig.custom()
.failureRateThreshold(50) // 失败率50%触发熔断
.slowCallRateThreshold(50) // 慢调用率50%触发熔断
.slowCallDurationThreshold(Duration.ofSeconds(2)) // 超过2秒算慢调用
.waitDurationInOpenState(Duration.ofSeconds(10)) // 熔断持续10秒
.slidingWindowSize(10) // 滑动窗口10次调用
.build();
}
3. 重试Filter
filters:
- name: Retry
args:
retries: 3 # 重试3次
statuses: BAD_GATEWAY # 502时重试
methods: GET # 只重试GET请求
backoff:
firstBackoff: 50ms # 首次重试延迟50ms
maxBackoff: 500ms # 最大延迟500ms
factor: 2 # 延迟倍数
basedOnPreviousValue: false
3.5 自定义Filter实践
3.5.1 权限校验Filter
@Component
@Order(-500)
public class PermissionFilter implements GlobalFilter, Ordered {
@Autowired
private PermissionService permissionService;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 提取用户上下文(由AuthenticationFilter放入)
Long userId = exchange.getAttribute("userId");
String userType = exchange.getAttribute("userType");
if (userId == null) {
// 未认证的请求,前置AuthenticationFilter会拦截,这里只是double check
return chain.filter(exchange);
}
// 2. 提取权限码(从路由元数据)
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
String permissionCode = (String) route.getMetadata().get("permissionCode");
if (StringUtils.isEmpty(permissionCode)) {
// 无需权限校验
return chain.filter(exchange);
}
// 3. 超级管理员直接放行
if ("SUPER_ADMIN".equals(userType)) {
return chain.filter(exchange);
}
// 4. 检查权限(三级缓存:Caffeine → Redis → DB)
String cacheKey = "permission:" + userId + ":" + permissionCode;
// L1: Caffeine本地缓存
Boolean hasPermission = LocalCache.get(cacheKey);
if (hasPermission != null) {
return hasPermission ? chain.filter(exchange) : forbiddenResponse(exchange);
}
// L2: Redis缓存
return Mono.fromCallable(() -> redisTemplate.opsForValue().get(cacheKey))
.flatMap(cached -> {
if (cached != null) {
Boolean permitted = (Boolean) cached;
LocalCache.put(cacheKey, permitted, 60, TimeUnit.SECONDS);
return permitted ? chain.filter(exchange) : forbiddenResponse(exchange);
}
// L3: 调用权限中心
return permissionService.checkPermission(userId, permissionCode)
.flatMap(permitted -> {
// 写入缓存
redisTemplate.opsForValue().set(cacheKey, permitted, 5, TimeUnit.MINUTES);
LocalCache.put(cacheKey, permitted, 60, TimeUnit.SECONDS);
return permitted ? chain.filter(exchange) : forbiddenResponse(exchange);
});
})
.onErrorResume(ex -> {
log.error("Permission check failed, userId={}, code={}", userId, permissionCode, ex);
// 降级策略:权限服务异常时放行(或拒绝,根据业务决定)
return chain.filter(exchange);
});
}
@Override
public int getOrder() {
return -500;
}
private Mono<Void> forbiddenResponse(ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.FORBIDDEN);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
String body = "{\"code\":\"403\",\"message\":\"Forbidden: No permission\"}";
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer));
}
}
路由配置中添加权限码:
routes:
- id: user-delete-route
uri: lb://user-service
predicates:
- Path=/api/user/{id}
- Method=DELETE
metadata:
permissionCode: "user:delete" # 权限码
filters:
- StripPrefix=2
3.5.2 响应时间监控Filter
@Component
@Order(1000) // 最后执行
public class MetricsGlobalFilter implements GlobalFilter, Ordered {
private static final String REQUEST_TIME_ATTR = "requestStartTime";
@Autowired
private MeterRegistry meterRegistry;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 记录开始时间
exchange.getAttributes().put(REQUEST_TIME_ATTR, System.currentTimeMillis());
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// 计算耗时
Long startTime = exchange.getAttribute(REQUEST_TIME_ATTR);
if (startTime != null) {
long duration = System.currentTimeMillis() - startTime;
// 提取元数据
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
String routeId = route != null ? route.getId() : "unknown";
String method = exchange.getRequest().getMethodValue();
int statusCode = exchange.getResponse().getStatusCode() != null
? exchange.getResponse().getStatusCode().value() : 0;
// 上报指标到Prometheus
meterRegistry.timer("gateway.request.duration",
"route", routeId,
"method", method,
"status", String.valueOf(statusCode)
).record(duration, TimeUnit.MILLISECONDS);
// 打印日志
log.info("Gateway metrics: route={}, method={}, status={}, duration={}ms",
routeId, method, statusCode, duration);
}
}));
}
@Override
public int getOrder() {
return 1000;
}
}
第三部分总结:Spring Cloud Gateway的核心是Route、Predicate、Filter三大概念,基于Project Reactor的响应式编程模型实现高并发。请求经过DispatcherHandler调度、RoutePredicateHandlerMapping匹配路由、FilteringWebHandler执行过滤器链,最终由NettyRoutingFilter转发到上游服务。通过自定义GlobalFilter,可以实现认证鉴权、权限校验、监控埋点等企业级功能。
第四部分:神话网关架构设计与实践
理论知识铺垫完毕,本部分将展示神话B2C电商平台API网关的真实架构设计和落地实践。我们如何在Spring Cloud Gateway基础上,构建一套企业级的动态路由管理系统?
4.1 控制面与数据面分离架构
借鉴Kubernetes、Istio等云原生系统的设计理念,神话网关采用**控制面(Control Plane)与数据面(Data Plane)**分离的架构。
4.1.1 整体架构
4.1.2 架构职责划分
| 组件 | 职责 | 技术选型 |
|---|---|---|
| 控制面(Admin) | 路由CRUD、版本管理、发布审批、操作审计 | Spring Boot + MyBatis-Plus + MySQL |
| 配置中心(Nacos) | 配置变更通知、Watch机制、秒级推送 | Nacos Config 2.3.x |
| 数据面(Gateway) | 流量转发、认证鉴权、限流熔断、无状态部署 | Spring Cloud Gateway + Reactor |
设计原则:
- 唯一数据源:MySQL是路由配置的唯一真实来源(Single Source of Truth)
- 配置推送:Nacos作为配置变更的通知渠道,不作为数据源
- 无状态数据面:Gateway实例不依赖MySQL,仅从Nacos加载配置
- 降级保护:Nacos不可用时,Gateway使用本地缓存启动
4.1.3 数据库表设计
核心表结构:
-- 应用管理表
CREATE TABLE `gateway_app` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
`app_code` VARCHAR(64) NOT NULL COMMENT '应用编码(唯一)',
`app_name` VARCHAR(128) NOT NULL COMMENT '应用名称',
`app_desc` VARCHAR(512) DEFAULT NULL COMMENT '应用描述',
`owner` VARCHAR(64) NOT NULL COMMENT '负责人工号',
`status` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态:0-禁用 1-启用',
`is_deleted` TINYINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '软删除',
`created_by` BIGINT UNSIGNED NOT NULL DEFAULT 0,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_by` BIGINT UNSIGNED NOT NULL DEFAULT 0,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_app_code_deleted` (`app_code`, `is_deleted`),
KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='网关应用表';
-- 路由配置表
CREATE TABLE `gateway_route` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`route_id` VARCHAR(128) NOT NULL COMMENT '路由ID(唯一)',
`app_id` BIGINT UNSIGNED NOT NULL COMMENT '所属应用',
`route_name` VARCHAR(128) NOT NULL COMMENT '路由名称',
`uri` VARCHAR(256) NOT NULL COMMENT '目标URI(lb://service-name)',
`order_num` INT NOT NULL DEFAULT 0 COMMENT '优先级(越小越高)',
`status` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态:0-禁用 1-启用',
`is_deleted` TINYINT UNSIGNED NOT NULL DEFAULT 0,
`created_by` BIGINT UNSIGNED NOT NULL DEFAULT 0,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_by` BIGINT UNSIGNED NOT NULL DEFAULT 0,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_route_id_deleted` (`route_id`, `is_deleted`),
KEY `idx_app_id_deleted` (`app_id`, `is_deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='网关路由表';
-- 路由断言表(一对多)
CREATE TABLE `gateway_route_predicate` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`route_id` VARCHAR(128) NOT NULL COMMENT '路由ID',
`predicate_name` VARCHAR(64) NOT NULL COMMENT '断言名称(Path/Method/Header等)',
`predicate_value` TEXT NOT NULL COMMENT '断言值(JSON格式)',
`order_num` INT NOT NULL DEFAULT 0 COMMENT '顺序',
PRIMARY KEY (`id`),
KEY `idx_route_id` (`route_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='路由断言配置';
-- 路由过滤器表(一对多)
CREATE TABLE `gateway_route_filter` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`route_id` VARCHAR(128) NOT NULL,
`filter_name` VARCHAR(64) NOT NULL COMMENT '过滤器名称',
`filter_config` TEXT COMMENT '过滤器配置(JSON)',
`order_num` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_route_id` (`route_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='路由过滤器配置';
-- 发布记录表
CREATE TABLE `gateway_publish_record` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`app_code` VARCHAR(64) NOT NULL COMMENT '应用编码',
`publish_type` TINYINT UNSIGNED NOT NULL COMMENT '发布类型:1-全量 2-增量',
`snapshot_content` LONGTEXT NOT NULL COMMENT '配置快照(JSON)',
`status` TINYINT UNSIGNED NOT NULL COMMENT '状态:0-发布中 1-成功 2-失败',
`error_msg` VARCHAR(512) DEFAULT NULL COMMENT '错误信息',
`created_by` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '发布人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '发布时间',
PRIMARY KEY (`id`),
KEY `idx_app_code_created_at` (`app_code`, `created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='发布记录表';
4.2 动态路由管理方案
神话网关的核心亮点是MySQL + Nacos双存储的动态路由方案,实现秒级热加载,无需重启。
4.2.1 配置发布流程
4.2.2 Nacos配置格式
应用分片列表(gateway-app-sharding-list.json):
{
"version": "1.0",
"updateTime": "2026-01-07T10:00:00Z",
"apps": [
{
"appCode": "user-service",
"enabled": true,
"updateTime": "2026-01-07T10:00:00Z"
},
{
"appCode": "order-service",
"enabled": true,
"updateTime": "2026-01-07T09:30:00Z"
}
]
}
单应用路由配置(gateway-route-user-service.json):
{
"appCode": "user-service",
"version": "20260107100000",
"routes": [
{
"id": "user-get-by-id",
"uri": "lb://user-service",
"order": 1,
"predicates": [
{
"name": "Path",
"args": {
"pattern": "/api/user/v1/users/{id}"
}
},
{
"name": "Method",
"args": {
"methods": ["GET"]
}
}
],
"filters": [
{
"name": "StripPrefix",
"args": {
"parts": 3
}
},
{
"name": "AddRequestHeader",
"args": {
"name": "X-Gateway-Route",
"value": "user-get-by-id"
}
}
],
"metadata": {
"permissionCode": "user:read",
"timeout": 5000
}
},
{
"id": "user-create",
"uri": "lb://user-service",
"order": 2,
"predicates": [
{
"name": "Path",
"args": {
"pattern": "/api/user/v1/users"
}
},
{
"name": "Method",
"args": {
"methods": ["POST"]
}
}
],
"filters": [
{
"name": "StripPrefix",
"args": {
"parts": 3
}
},
{
"name": "RequestRateLimiter",
"args": {
"redis-rate-limiter.replenishRate": 10,
"redis-rate-limiter.burstCapacity": 20,
"key-resolver": "#{@userKeyResolver}"
}
}
],
"metadata": {
"permissionCode": "user:create",
"timeout": 10000
}
}
],
"globalConfig": {
"defaultTimeout": 5000,
"maxRequestSize": "10MB"
}
}
4.2.3 DynamicRouteLoader核心代码
@Component
@Slf4j
public class DynamicRouteLoader implements ApplicationListener<ApplicationReadyEvent> {
@Autowired
private NacosConfigManager nacosConfigManager;
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
@Autowired
private ApplicationEventPublisher eventPublisher;
@Value("${spring.cloud.nacos.config.group:GATEWAY_ROUTER}")
private String nacosGroup;
private static final String APP_SHARDING_DATA_ID = "gateway-app-sharding-list.json";
private static final String ROUTE_DATA_ID_PREFIX = "gateway-route-";
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
log.info("Gateway starting, loading routes from Nacos...");
try {
// 1. 加载应用分片列表
String appShardingConfig = nacosConfigManager.getConfigService()
.getConfig(APP_SHARDING_DATA_ID, nacosGroup, 5000);
if (StringUtils.isEmpty(appShardingConfig)) {
log.warn("App sharding config not found, using local cache");
loadFromLocalCache();
return;
}
AppShardingList shardingList = JSON.parseObject(appShardingConfig, AppShardingList.class);
// 2. 为每个应用注册Listener
for (AppSharding app : shardingList.getApps()) {
if (!app.isEnabled()) {
continue;
}
String dataId = ROUTE_DATA_ID_PREFIX + app.getAppCode() + ".json";
// 初始化加载
loadRouteConfig(dataId);
// 注册变更监听
nacosConfigManager.getConfigService().addListener(dataId, nacosGroup, new Listener() {
@Override
public Executor getExecutor() {
return null; // 使用Nacos默认线程池
}
@Override
public void receiveConfigInfo(String configInfo) {
log.info("Route config changed for app: {}", app.getAppCode());
try {
loadRouteConfig(dataId);
} catch (Exception e) {
log.error("Failed to reload route config", e);
}
}
});
log.info("Registered Nacos listener for app: {}", app.getAppCode());
}
} catch (Exception e) {
log.error("Failed to load routes from Nacos, using local cache", e);
loadFromLocalCache();
}
}
private void loadRouteConfig(String dataId) throws Exception {
// 1. 从Nacos拉取配置
String config = nacosConfigManager.getConfigService().getConfig(dataId, nacosGroup, 5000);
if (StringUtils.isEmpty(config)) {
log.warn("Route config not found: {}", dataId);
return;
}
// 2. 解析JSON
RouteConfigDTO routeConfig = JSON.parseObject(config, RouteConfigDTO.class);
// 3. 清除旧路由(同appCode的)
clearOldRoutes(routeConfig.getAppCode());
// 4. 加载新路由
for (RouteDTO routeDTO : routeConfig.getRoutes()) {
RouteDefinition routeDefinition = convertToRouteDefinition(routeDTO);
routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
log.info("Loaded route: {}", routeDefinition.getId());
}
// 5. 发布刷新事件
eventPublisher.publishEvent(new RefreshRoutesEvent(this));
// 6. 保存到本地缓存
saveToLocalCache(dataId, config);
log.info("Successfully loaded {} routes for app: {}",
routeConfig.getRoutes().size(), routeConfig.getAppCode());
}
private RouteDefinition convertToRouteDefinition(RouteDTO dto) {
RouteDefinition definition = new RouteDefinition();
definition.setId(dto.getId());
definition.setUri(URI.create(dto.getUri()));
definition.setOrder(dto.getOrder());
// Predicates
List<PredicateDefinition> predicates = dto.getPredicates().stream()
.map(p -> {
PredicateDefinition pd = new PredicateDefinition();
pd.setName(p.getName());
pd.setArgs(p.getArgs());
return pd;
})
.collect(Collectors.toList());
definition.setPredicates(predicates);
// Filters
List<FilterDefinition> filters = dto.getFilters().stream()
.map(f -> {
FilterDefinition fd = new FilterDefinition();
fd.setName(f.getName());
fd.setArgs(f.getArgs());
return fd;
})
.collect(Collectors.toList());
definition.setFilters(filters);
// Metadata
definition.setMetadata(dto.getMetadata());
return definition;
}
private void clearOldRoutes(String appCode) {
// 实现略:删除内存中该应用的所有路由
}
private void loadFromLocalCache() {
// 实现略:从/data/gateway/routes/加载缓存文件
}
private void saveToLocalCache(String dataId, String config) {
// 实现略:写入本地文件作为降级缓存
Path cacheFile = Paths.get("/data/gateway/routes/" + dataId);
try {
Files.createDirectories(cacheFile.getParent());
Files.write(cacheFile, config.getBytes(StandardCharsets.UTF_8));
log.info("Saved route config to local cache: {}", cacheFile);
} catch (IOException e) {
log.error("Failed to save local cache", e);
}
}
}
4.3 认证鉴权体系
神话网关支持多用户类型(C端客户、B端员工、供应商、合作伙伴)的统一认证和细粒度权限控制。
4.3.1 认证流程
4.3.2 AuthenticationFilter实现
@Component
@Order(-998)
@Slf4j
public class AuthenticationFilter implements GlobalFilter, Ordered {
@Autowired
private SsoClient ssoClient; // Feign客户端
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String TOKEN_CACHE_PREFIX = "gateway:token:";
private static final long TOKEN_CACHE_TTL = 30 * 60; // 30分钟
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 1. 提取Token
String token = extractToken(request);
if (StringUtils.isEmpty(token)) {
log.warn("No token found in request");
return unauthorizedResponse(exchange);
}
// 2. Token缓存查询
String cacheKey = TOKEN_CACHE_PREFIX + token;
return Mono.fromCallable(() -> redisTemplate.opsForValue().get(cacheKey))
.flatMap(cached -> {
if (cached != null) {
// 缓存命中
UserContext userContext = (UserContext) cached;
return handleAuthenticatedUser(exchange, chain, userContext);
}
// 3. 调用SSO验证Token
return ssoClient.validateToken(token)
.flatMap(userContext -> {
// 写入缓存
redisTemplate.opsForValue().set(cacheKey, userContext, TOKEN_CACHE_TTL, TimeUnit.SECONDS);
return handleAuthenticatedUser(exchange, chain, userContext);
})
.onErrorResume(ex -> {
log.error("Token validation failed: {}", token, ex);
return forbiddenResponse(exchange);
});
})
.switchIfEmpty(Mono.defer(() -> {
// Redis查询返回空(不太可能)
return ssoClient.validateToken(token)
.flatMap(userContext -> {
redisTemplate.opsForValue().set(cacheKey, userContext, TOKEN_CACHE_TTL, TimeUnit.SECONDS);
return handleAuthenticatedUser(exchange, chain, userContext);
});
}));
}
private Mono<Void> handleAuthenticatedUser(ServerWebExchange exchange, GatewayFilterChain chain,
UserContext userContext) {
// 放入上下文供后续Filter使用
exchange.getAttributes().put("userId", userContext.getUserId());
exchange.getAttributes().put("userType", userContext.getUserType());
exchange.getAttributes().put("roles", userContext.getRoles());
// 添加Header传递给下游服务
ServerHttpRequest mutatedRequest = exchange.getRequest().mutate()
.header("X-User-Id", String.valueOf(userContext.getUserId()))
.header("X-User-Type", userContext.getUserType())
.header("X-User-Roles", String.join(",", userContext.getRoles()))
.build();
return chain.filter(exchange.mutate().request(mutatedRequest).build());
}
private String extractToken(ServerHttpRequest request) {
String authorization = request.getHeaders().getFirst("Authorization");
if (StringUtils.hasText(authorization) && authorization.startsWith("Bearer ")) {
return authorization.substring(7);
}
return null;
}
@Override
public int getOrder() {
return -998;
}
// unauthorizedResponse、forbiddenResponse方法略
}
4.4 限流熔断机制
4.4.1 Sentinel集成
神话网关集成Alibaba Sentinel实现限流熔断,规则存储在Nacos动态配置。
pom.xml依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
application.yml配置:
spring:
cloud:
sentinel:
transport:
dashboard: sentinel-dashboard:8080 # Sentinel控制台
datasource:
# 流控规则
flow:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
dataId: gateway-sentinel-flow-rules.json
groupId: SENTINEL_GROUP
rule-type: gw-flow
# 熔断降级规则
degrade:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
dataId: gateway-sentinel-degrade-rules.json
groupId: SENTINEL_GROUP
rule-type: gw-degrade
Sentinel流控规则(Nacos配置):
[
{
"resource": "user-service-route",
"resourceMode": 0,
"grade": 1,
"count": 100,
"intervalSec": 1,
"controlBehavior": 0,
"burst": 20,
"maxQueueingTimeMs": 500,
"paramItem": {
"parseStrategy": 3,
"fieldName": "userId"
}
}
]
Sentinel熔断规则:
[
{
"resource": "order-service-route",
"grade": 0,
"count": 0.5,
"timeWindow": 10,
"minRequestAmount": 5,
"statIntervalMs": 1000,
"slowRatioThreshold": 0.5
}
]
4.4.2 自定义降级响应
@Configuration
public class SentinelConfig {
@PostConstruct
public void init() {
// 自定义限流降级响应
GatewayCallbackManager.setBlockHandler((exchange, t) -> {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
Map<String, Object> result = new HashMap<>();
result.put("code", "429");
result.put("message", "请求过于频繁,请稍后重试");
result.put("traceId", exchange.getAttribute("traceId"));
result.put("timestamp", System.currentTimeMillis());
String body = JSON.toJSONString(result);
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(buffer));
});
}
}
4.5 灰度发布方案
4.5.1 基于Header的灰度路由
routes:
# 灰度路由(优先级高)
- id: user-service-v2-canary
uri: lb://user-service-v2
order: 1
predicates:
- Path=/api/user/**
- Header=X-Canary-Version, v2 # 请求头指定版本
filters:
- StripPrefix=2
- AddResponseHeader=X-Served-By, user-service-v2
# 稳定路由
- id: user-service-v1-stable
uri: lb://user-service-v1
order: 10
predicates:
- Path=/api/user/**
filters:
- StripPrefix=2
- AddResponseHeader=X-Served-By, user-service-v1
4.5.2 基于用户ID的灰度
自定义GrayFilter实现:
@Component
@Order(200)
public class GrayFilter implements GlobalFilter, Ordered {
@Autowired
private GrayRuleRepository grayRuleRepository;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
Long userId = exchange.getAttribute("userId");
if (userId == null) {
return chain.filter(exchange);
}
// 查询灰度规则
return grayRuleRepository.findByRouteId(route.getId())
.flatMap(grayRule -> {
if (isUserInGrayList(userId, grayRule)) {
// 修改URI到灰度版本
URI grayUri = URI.create(grayRule.getGrayUri());
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, grayUri);
exchange.getResponse().getHeaders().add("X-Gray-Version", grayRule.getGrayVersion());
}
return chain.filter(exchange);
})
.switchIfEmpty(chain.filter(exchange));
}
private boolean isUserInGrayList(Long userId, GrayRule grayRule) {
if (grayRule.getGrayType() == GrayType.USER_ID_LIST) {
return grayRule.getUserIds().contains(userId);
} else if (grayRule.getGrayType() == GrayType.USER_ID_MOD) {
// 用户ID模10取余
return userId % 10 < grayRule.getGrayPercentage() / 10;
}
return false;
}
@Override
public int getOrder() {
return 200;
}
}
4.6 监控与链路追踪
4.6.1 Prometheus指标采集
集成Micrometer Prometheus:
management:
endpoints:
web:
exposure:
include: prometheus,health,info
metrics:
tags:
application: mythos-gateway
env: ${spring.profiles.active}
export:
prometheus:
enabled: true
MetricsGlobalFilter采集的核心指标:
| 指标名 | 类型 | 说明 | 标签 |
|---|---|---|---|
gateway.request.duration |
Timer | 请求耗时分布 | route, method, status |
gateway.request.total |
Counter | 请求总数 | route, method |
gateway.request.error |
Counter | 错误请求数 | route, error_type |
gateway.route.active |
Gauge | 当前活跃路由数 | - |
Grafana Dashboard示例查询:
# P99响应时间
histogram_quantile(0.99, sum(rate(gateway_request_duration_bucket[5m])) by (le, route))
# QPS(按路由)
sum(rate(gateway_request_total[1m])) by (route)
# 错误率
sum(rate(gateway_request_error[5m])) by (route) / sum(rate(gateway_request_total[5m])) by (route)
4.6.2 链路追踪
集成Spring Cloud Sleuth + Zipkin:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
spring:
sleuth:
sampler:
probability: 0.1 # 采样率10%
zipkin:
base-url: http://zipkin-server:9411
sender:
type: web
TraceIdFilter确保TraceId传递:
- 生成TraceId:
trace-{timestamp}-{random} - 添加到响应头:
X-Trace-Id - 传递给下游服务:
X-B3-TraceId(Zipkin标准) - 记录到日志:通过MDC自动注入
第四部分总结:神话网关采用控制面与数据面分离架构,通过MySQL存储配置、Nacos推送变更实现秒级动态路由。11个GlobalFilter构成完整的过滤器链,涵盖TraceId生成、JWT认证、权限校验、Sentinel限流熔断、灰度路由、指标采集等企业级功能。通过Prometheus + Grafana + Zipkin构建全方位的可观测性体系。
总结与展望
核心收获
本文从理论到实践,全面剖析了Spring Cloud Gateway从选型、设计到落地的完整过程。通过神话B2C电商平台的真实案例,我们看到了一套企业级API网关应该具备的能力:
架构设计:
- ✅ 控制面与数据面分离:借鉴云原生设计理念,实现配置管理和流量转发的解耦
- ✅ MySQL + Nacos双存储:保证配置可靠性的同时,实现秒级动态路由刷新
- ✅ 无状态数据面:支持水平扩展,轻松应对流量洪峰
核心功能:
- ✅ 11个GlobalFilter过滤器链:TraceId → 认证 → 鉴权 → 限流 → 熔断 → 灰度 → 监控,形成完整的请求处理流水线
- ✅ 响应式高并发:基于Reactor + Netty,单机QPS达到5万+
- ✅ 多租户权限体系:支持C端、B端、供应商等多用户类型的统一认证
生产保障:
- ✅ Kubernetes云原生部署:弹性伸缩、滚动更新、多机房容灾
- ✅ 全方位可观测性:Prometheus指标 + Zipkin链路追踪 + 日志聚合
- ✅ 多层安全防护:IP黑白名单、防刷机制、敏感信息脱敏
技术选型建议
| 场景 | 推荐方案 | 核心理由 |
|---|---|---|
| Java技术栈 | Spring Cloud Gateway | 生态集成好、学习成本低、调试便利 |
| 极致性能要求 | Apache APISIX | QPS 100k+、配置毫秒级生效 |
| 成熟插件生态 | Kong | 50+官方插件、企业级支持 |
| 混合语言环境 | APISIX/Kong | 多协议支持、语言无关 |
未来演进方向
1. 服务网格融合
随着Istio、Linkerd等服务网格技术的成熟,未来网关可能与Service Mesh深度融合:
- 南北流量 + 东西流量统一治理
- API网关下沉到Sidecar
- 统一的流量策略和可观测性
2. AI驱动的智能网关
- 智能路由:基于ML模型预测流量,动态调整路由权重
- 异常检测:AI识别异常流量模式,自动触发防护策略
- 自适应限流:根据系统负载和历史数据,动态调整限流阈值
3. Serverless网关
- 按需启动:流量到来时才启动网关实例(冷启动优化)
- 按调用计费:降低小流量场景的成本
- 函数级路由:直接路由到FaaS函数
4. WebAssembly插件化
- 多语言插件:使用Rust、Go、AssemblyScript编写高性能插件
- 沙箱隔离:WASM运行时提供安全隔离
- 热更新:插件代码变更无需重启网关
参考资料
官方文档:
开源项目:
更多推荐


所有评论(0)