微服务网关:Spring Cloud Gateway 路由与限流

在微服务架构中,API 网关作为系统的统一入口,承担着路由转发、负载均衡、认证鉴权、限流熔断等重要职责。Spring Cloud Gateway 作为 Spring Cloud 生态中的第二代网关,基于 Spring 5、Spring Boot 2 和 Project Reactor 构建,旨在取代 Netflix Zuul,提供更高效、更灵活的网关解决方案。

一、Spring Cloud Gateway 架构设计

1.1 核心概念

Spring Cloud Gateway 的设计围绕三个核心概念展开:

  • Route(路由):网关的基本映射单元,包含 ID、目标 URI、断言集合和过滤器集合
  • Predicate(断言):匹配条件,决定请求是否被路由到特定目标
  • Filter(过滤器):对请求和响应进行处理,可实现日志记录、鉴权、限流等功能

1.2 工作流程

Client → Gateway Handler → Predicate Matching → Filter Chain → Target Service
                                    ↑              ↓
                              Route Locator    Response Filters

Gateway 接收请求后,通过路由定位器查找匹配的路由,执行断言判断,通过后进入过滤器链处理,最终转发到目标服务。

二、路由配置详解

2.1 静态路由配置

通过 YAML 配置文件定义路由是最常用的方式:

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
            - Method=GET,POST
          filters:
            - StripPrefix=1
            - AddRequestHeader=X-Request-Source,Gateway
            
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**
            - Query=version,v1
          filters:
            - CircuitBreaker=name=orderCircuitBreaker,fallbackUri=forward:/fallback/order

关键配置说明

  • lb:// 表示使用负载均衡从服务注册中心获取实例
  • StripPrefix 去掉路径前缀,如 /api/users/list/users/list
  • CircuitBreaker 集成熔断器,失败时转发到降级接口

2.2 动态路由配置

生产环境通常需要动态更新路由,可通过 RouteDefinitionRepository 实现:

@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {
    
    private final String ROUTE_KEY = "gateway:routes";
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        List<RouteDefinition> routes = new ArrayList<>();
        redisTemplate.opsForHash().values(ROUTE_KEY).forEach(route -> {
            routes.add(JSON.parseObject(route.toString(), RouteDefinition.class));
        });
        return Flux.fromIterable(routes);
    }
    
    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return route.flatMap(r -> {
            redisTemplate.opsForHash().put(ROUTE_KEY, r.getId(), 
                JSON.toJSONString(r));
            return Mono.empty();
        });
    }
    
    // 发布路由刷新事件
    public void publishRefreshEvent() {
        applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this));
    }
}

通过 Redis 存储路由定义,结合 Spring 事件机制实现运行时热更新,无需重启网关。

2.3 自定义断言

当内置断言无法满足需求时,可自定义断言工厂:

@Component
public class TokenVersionRoutePredicateFactory extends 
    AbstractRoutePredicateFactory<TokenVersionRoutePredicateFactory.Config> {
    
    public TokenVersionRoutePredicateFactory() {
        super(Config.class);
    }
    
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            String token = exchange.getRequest()
                .getHeaders().getFirst("X-Token-Version");
            return config.getVersion().equals(token);
        };
    }
    
    @Data
    public static class Config {
        private String version;
    }
}

// 使用方式
// predicates:
//   - TokenVersion=v2

三、限流机制实现

3.1 基于 Redis 的分布式限流

Spring Cloud Gateway 集成 RequestRateLimiter 过滤器,基于 Redis 实现令牌桶算法:

spring:
  cloud:
    gateway:
      routes:
        - id: rate_limited_route
          uri: lb://user-service
          predicates:
            - Path=/api/**
          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
public KeyResolver userKeyResolver() {
    // 按用户IP限流
    return exchange -> Mono.just(
        exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
    );
}

@Bean
public KeyResolver apiKeyResolver() {
    // 按API路径限流
    return exchange -> Mono.just(
        exchange.getRequest().getPath().value()
    );
}

3.2 自定义限流过滤器

更复杂的限流策略可通过自定义过滤器实现:

@Component
public class CustomRateLimitFilter extends AbstractGatewayFilterFactory<CustomRateLimitFilter.Config> {
    
    @Autowired
    private ReactiveRedisTemplate<String, String> redisTemplate;
    
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String key = getLimitKey(exchange, config);
            
            return redisTemplate.opsForValue()
                .increment(key)
                .flatMap(count -> {
                    if (count == 1) {
                        // 首次访问,设置过期时间
                        return redisTemplate.expire(key, Duration.ofSeconds(config.getWindow()))
                            .thenReturn(count);
                    }
                    return Mono.just(count);
                })
                .flatMap(count -> {
                    if (count > config.getMaxRequests()) {
                        // 触发限流
                        exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
                        exchange.getResponse().getHeaders()
                            .add("X-RateLimit-Limit", String.valueOf(config.getMaxRequests()));
                        exchange.getResponse().getHeaders()
                            .add("X-RateLimit-Remaining", "0");
                        return exchange.getResponse().setComplete();
                    }
                    
                    // 添加限流响应头
                    exchange.getResponse().getHeaders()
                        .add("X-RateLimit-Limit", String.valueOf(config.getMaxRequests()));
                    exchange.getResponse().getHeaders()
                        .add("X-RateLimit-Remaining", 
                            String.valueOf(config.getMaxRequests() - count));
                    
                    return chain.filter(exchange);
                });
        };
    }
    
    private String getLimitKey(ServerWebExchange exchange, Config config) {
        String identifier = exchange.getRequest().getHeaders().getFirst("X-User-Id");
        if (identifier == null) {
            identifier = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
        }
        return String.format("rate:%s:%s", config.getType(), identifier);
    }
    
    @Data
    public static class Config {
        private int maxRequests = 100;  // 窗口期内最大请求数
        private int window = 60;        // 时间窗口(秒)
        private String type = "default"; // 限流类型
    }
}

3.3 分级限流策略

生产环境建议采用分级限流:

层级策略阈值用途
全局基于CPU/内存负载动态调整保护网关自身
服务基于目标服务容量服务注册元数据保护下游服务
接口基于API重要性配置中心核心业务保护
用户基于用户等级用户标签差异化服务

四、高级特性与最佳实践

4.1 请求/响应改写

@Component
public class RequestModifyFilter implements GlobalFilter, Ordered {
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 添加 traceId 用于全链路追踪
        String traceId = UUID.randomUUID().toString().replace("-", "");
        ServerHttpRequest request = exchange.getRequest().mutate()
            .header("X-Trace-Id", traceId)
            .header("X-Request-Time", String.valueOf(System.currentTimeMillis()))
            .build();
        
        return chain.filter(exchange.mutate().request(request).build())
            .then(Mono.fromRunnable(() -> {
                // 响应后处理
                Long startTime = Long.parseLong(
                    exchange.getRequest().getHeaders().getFirst("X-Request-Time"));
                long duration = System.currentTimeMillis() - startTime;
                log.info("TraceId: {}, Duration: {}ms", traceId, duration);
            }));
    }
    
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE; // 最先执行
    }
}

4.2 灰度发布支持

@Component
public class GrayReleaseFilter extends AbstractGatewayFilterFactory<Config> {
    
    @Autowired
    private GrayReleaseService grayService;
    
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
            
            // 判断用户是否在灰度名单
            if (grayService.isGrayUser(userId, config.getService())) {
                // 修改路由目标到灰度版本
                ServerHttpRequest request = exchange.getRequest().mutate()
                    .header("X-Gray-Version", "v2")
                    .build();
                
                // 通过元数据选择灰度实例
                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR,
                    URI.create("lb://" + config.getService() + "-gray"));
                
                return chain.filter(exchange.mutate().request(request).build());
            }
            
            return chain.filter(exchange);
        };
    }
}

4.3 性能优化建议

  1. 合理配置工作线程:根据 CPU 核心数调整 reactor.netty.ioWorkerCount
  2. 启用 HTTP 压缩:减少网络传输开销
  3. 使用本地缓存:热点路由配置可本地缓存,减少 Redis 访问
  4. 连接池优化:调整 HttpClient 连接池大小和超时配置
  5. 监控指标采集:集成 Micrometer 暴露网关指标

五、总结

Spring Cloud Gateway 作为响应式网关的代表,通过灵活的路由配置、丰富的过滤器机制和强大的限流能力,为微服务架构提供了可靠的入口保护。在实际应用中,建议结合配置中心实现动态路由管理,采用多级限流策略保障系统稳定性,并通过全链路追踪和监控指标持续优化网关性能。

随着云原生技术的发展,Spring Cloud Gateway 与 Kubernetes Ingress、Service Mesh 的集成将成为新的探索方向,为微服务治理提供更加完善的解决方案。