Spring Cloud Gateway案例

wen java案例 1

从零构建高可用API网关:Spring Cloud Gateway在生产环境的5个实战案例

目录导读

  1. 为什么选择Spring Cloud Gateway替代Zuul?
  2. 基于路由规则的灰度发布实践
  3. 全局过滤器实现JWT鉴权与限流
  4. WebSocket代理与长连接保持
  5. 跨域CORS配置与安全Header处理
  6. 动态路由与Nacos配置中心联动
  7. 常见问题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、限流、路径重写等高级特性。

Spring Cloud Gateway案例

核心优势对比:

  • 异步非阻塞:底层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 GatewaySecureHeaders过滤器(通过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时需要把predicatesfilters转换为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个案例涵盖了灰度、鉴权、限流、长连接、动态路由等高频需求,建议结合自身业务逐步引入,避免一次性全面改造带来的风险。

抱歉,评论功能暂时关闭!