Spring Cloud Gateway 深度解析:从路由原理到生产级网关实战

Spring Cloud Gateway 深度解析:从路由原理到生产级网关实战

摘要 :Spring Cloud Gateway 作为 Spring 官方推荐的 API 网关,构建于 Project Reactor 和 Netty 之上,提供了高性能的响应式路由能力。本文从 GatewayProperties 的配置加载出发,深入解析 RouteDefinitionRouteLocator 的路由解析、FilteringWebHandler 的过滤器链编排、RoutePredicateHandlerMapping 的请求匹配机制,以及动态路由、自定义过滤器、限流熔断等生产级实战方案。


一、引言

在微服务架构中,API 网关是整个系统的统一入口。它承担着请求路由、负载均衡、认证鉴权、限流熔断、日志监控等横切关注点。Spring Cloud Gateway 取代 Netflix Zuul 成为 Spring 生态的官方网关方案,其核心优势在于:

  1. 响应式架构:基于 WebFlux 和 Netty,使用少量线程处理大量并发连接
  2. 统一过滤器链:支持全局过滤器和路由级过滤器的组合编排
  3. 动态路由:支持从配置中心(Nacos/Consul)实时刷新路由规则
  4. 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 生态 通用 高性能场景

相关推荐
Simon523142 小时前
Spring Bean----5.27学习小记
java·学习·spring
ZJH__GO2 小时前
java项目-流水线线程池
java·开发语言
●VON2 小时前
鸿蒙NEXT ArkUI进阶:用CustomBuilder打造高定制化品牌页签栏
java·华为·harmonyos·鸿蒙·新特性
夕除2 小时前
spring boot 16
java·spring boot·后端
努力成为AK大王2 小时前
Spring Bean 作用域与生命周期
java·后端·spring
希望永不加班2 小时前
SpringBoot 消息幂等性设计:防重复消费
java·开发语言·spring boot·后端·spring
我是一颗柠檬2 小时前
【JDK8新特性】CompletableFuture异步编程Day10
java·开发语言·后端·intellij-idea
a23121212 小时前
从零搭建Spring Ai多智能体后端应用
java·运维·微服务·多智能体·后端开发·spring ai
Yeats_Liao3 小时前
5:Servlet程序-Java Web
java·后端·设计