从零构建高可用API网关:Spring Cloud Gateway在生产环境的5个实战案例
目录导读
- 为什么选择Spring Cloud Gateway替代Zuul?
- 基于路由规则的灰度发布实践
- 全局过滤器实现JWT鉴权与限流
- WebSocket代理与长连接保持
- 跨域CORS配置与安全Header处理
- 动态路由与Nacos配置中心联动
- 常见问题Q&A(问答环节)
为什么选择Spring Cloud Gateway替代Zuul?
Spring Cloud Gateway基于Spring WebFlux,采用非阻塞IO模型,性能上远超Zuul 1.x的Servlet阻塞模型,根据Spring官方基准测试,Gateway的吞吐量比Zuul高约1.6倍,延迟降低35%,Gateway天然集成Spring Security 5、Reactive Ribbon和Hystrix,支持WebSocket、限流、路径重写等高级特性。

核心优势对比:
- 异步非阻塞:底层Netty处理高并发,线程数固定,避免线程膨胀。
- 精细路由:通过
RoutePredicateFactory实现时间、Header、Query、Method等条件组合。 - 过滤器链:
GlobalFilter+GatewayFilter实现横切逻辑,如日志、鉴权、限流。
基于路由规则的灰度发布实践
背景:某电商平台需要将10%流量导向新版本服务(order-service-v2),其余流量继续访问旧版。
实现方案:通过Weight路由谓词实现权重分配。
spring:
cloud:
gateway:
routes:
- id: order-service-v1
uri: lb://order-service-v1
predicates:
- Weight=group1, 9
- id: order-service-v2
uri: lb://order-service-v2
predicates:
- Weight=group1, 1
关键点:Weight谓词基于Spring的WeightCalculatorWebFilter,按权重比例计算随机数,若需按用户IP或用户ID做灰度,可自定义GrayPredicateFactory,读取请求头(如X-User-ID)进行hash取模。
优化建议:灰度发布时需配合Retry过滤器,防止v2节点启动未完成导致的短暂失败,同时为v2设置Timeout超时时间为500ms,避免旧逻辑拖慢整体响应。
全局过滤器实现JWT鉴权与限流
场景:所有非白名单请求必须携带有效JWT Token,且每个接口限流QPS=100。
JWT鉴权过滤器
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
// 解析JWT,校验签名与过期时间
try {
Jwt jwt = JwtHelper.decodeAndVerify(token, signingKey);
// 将用户ID写入Header传递下游
exchange = exchange.mutate()
.request(r -> r.header("X-User-Id", jwt.getClaims()))
.build();
return chain.filter(exchange);
} catch (Exception e) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}
@Override
public int getOrder() { return -100; } // 优先执行
}
令牌桶限流过滤器
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: "#{@userKeyResolver}"
自定义key-resolver:根据请求参数中的userId限流,避免全局共享限流导致恶意用户饿死他人。
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getQueryParams().getFirst("userId")
);
}
WebSocket代理与长连接保持
挑战:Nginx对WebSocket的断线重连支持不友好,而Gateway原生支持ws协议。
配置示例:
spring:
cloud:
gateway:
routes:
- id: ws-chat
uri: lb:ws://chat-service # 使用ws前缀
predicates:
- Path=/chat/**
filters:
- PreserveHostHeader
关键特性:
NettyRoutingFilter自动处理WebSocket握手,无需额外编码。- 通过
WsProxyFilter维持心跳,默认每30秒发送Ping,若空闲超时则断开。 - 连接保持:需设置
spring.cloud.gateway.httpclient.websocket.max-frame-payload-length增加单帧字节限制,防止大消息被截断。
踩坑提示:如果上游服务使用Tomcat而非Reactor Netty,需确保上游支持WebSocket,且chat-service的/chat/** handler正确返回WebSocketHandler。
跨域CORS配置与安全Header处理
需求:前端域名 app.example.com 需要调用网关API,且需启用凭证模式(credentials)。
全局CORS配置:
spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "https://app.example.com"
allowedMethods: "GET,POST,PUT,DELETE,OPTIONS"
allowedHeaders: "*"
allowCredentials: true
maxAge: 3600
基于路由的CORS(更精细):
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/**
filters:
- DedupeResponseHeader=Access-Control-Allow-Origin, RETAIN_UNIQUE
安全Header增强:在application.yml添加X-Content-Type-Options, Strict-Transport-Security等Header,可通过自定义HeaderFilter或使用Spring Cloud Gateway的SecureHeaders过滤器(通过spring.cloud.gateway.filter.secure-headers.enabled=true激活)。
动态路由与Nacos配置中心联动
痛点:传统路由写死在配置文件,每次修改需重启网关,生产环境要求无需重启即可动态改变路由。
实现方案:使用Nacos监听配置变化,动态重新加载路由。
@Configuration
public class DynamicRouteConfig {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
@NacosConfigListener(dataId = "gateway-routes", groupId = "DEFAULT_GROUP", timeout = 5000)
public void onRouteChange(String configContent) {
List<RouteDefinition> newRoutes = parseJson(configContent);
// 先删除旧路由,再添加新路由
RouteDefinitionMatcher matcher = new RouteDefinitionMatcher();
routeDefinitionWriter.delete(Mono.just("order-service")).subscribe();
newRoutes.forEach(route -> {
routeDefinitionWriter.save(Mono.just(route)).subscribe();
});
}
}
解析JSON时需要把predicates和filters转换为Map结构,可采用JSON.parseObject + TypeReference。
注意事项:动态更新需考虑事务性和原子性,最好先保存一份备份路由,若解析失败则回滚到旧配置。
常见问题Q&A(问答环节)
Q1:Gateway和FeignClient如何配合使用?
- Feign是服务间调用(内部),Gateway是外部调用(北向流量),Gateway将请求路由到微服务后,微服务内部仍可用Feign调用其他依赖。必须确保Gateway不参与业务逻辑,只做协议转换和路由转发。
Q2:如何处理请求体中的JSON大字段?
- 需要重写
RequestBody时,可使用ModifyRequestBodyGatewayFilterFactory,该过滤器会缓存整个请求体,性能略差,建议对于超过1MB的body,改用ReadBodyPredicateFactory或自定义过滤器,避免内存溢出。
Q3:生产环境如何监控Gateway状态?
- 集成
Actuator暴露/gateway/routes、/gateway/globalfilters端点,将指标接入Prometheus + Grafana,关键监控项:gateway_requests_seconds(请求延迟)、gateway_requests_count(QPS)、连接池连接数。
Q4:Gateway宕机后如何保证高可用?
- 部署多个实例,前端用Nginx或云负载均衡(SLB)做健康检查,将请求分发到多个网关节点,网关本身无状态,Session状态全部存入Redis(如限流令牌桶),可水平扩展。
Q5:异常时的统一错误响应格式?
- 在全局
GlobalFilter中捕获Exception,返回统一JSON结构:{ "code": 500, "message": "Internal Server Error", "path": "/api/xx" },需注意WebFlux的@RestControllerAdvice只对Controller生效,对Gateway过滤器异常需单独处理。
Spring Cloud Gateway已成为微服务网关的事实标准,其响应式模型和丰富的谓词/过滤器生态能支撑大部分生产场景,以上5个案例涵盖了灰度、鉴权、限流、长连接、动态路由等高频需求,建议结合自身业务逐步引入,避免一次性全面改造带来的风险。