第一章:Spring Cloud Gateway 概述与核心概念
1.1 什么是 Spring Cloud Gateway
Spring Cloud Gateway 是基于 Spring 5、Spring Boot 2 和 Project Reactor 构建的 API 网关。它提供了一种简单而有效的方式来路由到 API 并为它们提供横切关注点,如:安全性、监控/指标和弹性。
1.2 核心概念
| 概念 | 说明 | |: --- | --- | | Route(路由) | 网关的基本构建块,定义了请求匹配规则和转发目标。包含唯一 ID、目标 URI、断言集合、过滤器集合等。 | | Predicate(断言) | 用于匹配 HTTP 请求中的某些属性(如路径、方法、头信息等),决定是否应用该路由。 | | Filter(过滤器) | 在请求被路由前或后修改请求或响应。分为”前置过滤器”和”后置过滤器”。 | | GatewayFilter | 特定于某个路由的过滤器,可链式处理。 | | GlobalFilter | 全局生效的过滤器,对所有路由起作用。 |
第二章:环境搭建与基础配置
2.1 添加依赖与启动类配置
创建 Spring Boot 项目并引入 spring-cloud-starter-gateway 依赖。
Maven 配置示例:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
确保不引入 spring-boot-starter-web,避免与 Netty 冲突。
2.2 基础路由配置方式
支持两种配置方式:Java 代码配置和 YAML 配置。
第三章:路由定义与断言工厂(Route & Predicate Factories)
3.1 RouteLocator 与自定义路由配置
通过 RouteLocatorBuilder 构建路由规则,调用 RouteLocatorBuilder.Builder.route() 方法。
| 方法名 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| id | route().id(String id) | 设置路由唯一标识 | id("user-service-route") | 建议命名清晰,便于排查问题 |
| path | route().path(String... patterns) | 根据请求路径匹配路由 | path("/user/**") | 支持 Ant 风格路径,如 /**、/user/*/info |
| uri | route().uri(URI uri) 或 uri(String uri) | 设置目标服务地址 | uri("http://localhost:8081") | 可使用 lb://service-name 实现负载均衡 |
| filter | route().filter(GatewayFilter filter, int order) | 添加 GatewayFilter 过滤器 | filter(new CustomFilter(), 1) | order 越小优先级越高 |
| filters | route().filters(GatewayFilterSpec spec) | 批量添加过滤器 | filters(f -> f.stripPrefix(1).addRequestHeader("X-Request-Foo", "Bar")) | 常用于链式配置多个过滤器 |
| predicate | route().predicate(Predicate predicate) | 自定义断言逻辑 | predicate(exchange -> exchange.getRequest().getHeaders().containsKey("Authorization")) | 高级用法,一般使用内置工厂更方便 |
| and | route().and() | 组合多个条件(逻辑与) | path("/api/**").and().method(GET) | 多个条件必须同时满足 |
| or | route().or() | 组合多个条件(逻辑或) | path("/admin/**").or().header("X-Admin-Flag") | Reactor 不支持直接 or,需用 .or() 方法 |
3.2 内置断言工厂(Predicate Factories)
Spring Cloud Gateway 提供多种内置断言工厂,均以 xxxRoutePredicateFactory 形式存在。
| 断言工厂 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| Path | path(String... patterns) | 匹配请求路径 | path("/product/**", "/order/**") | 最常用,支持多路径匹配 |
| Method | method(HttpMethod... methods) | 匹配 HTTP 方法 | method(GET, POST) | 可指定一个或多个方法类型 |
| Header | header(String headerName, String regex) | 匹配请求头是否存在或符合正则 | header("X-Auth-Token", "\\d+") | 第二个参数为可选正则表达式 |
| Host | host(String... patterns) | 匹配 Host 请求头 | host("**.example.com") | 支持通配符 * 和 ** |
| Query | query(String param, String regex) | 匹配查询参数是否存在或值符合正则 | query("name", ".john.") | 如只传参数名,则判断是否存在 |
| Cookie | cookie(String name, String regex) | 匹配 Cookie 名称及值 | cookie("JSESSIONID", "\\w+") | 常用于会话识别 |
| After | after(ZonedDateTime datetime) | 匹配时间之后的请求 | after(ZonedDateTime.now().plusHours(1)) | 常用于灰度发布 |
| Before | before(ZonedDateTime datetime) | 匹配时间之前的请求 | before(ZonedDateTime.parse("2025-01-01T00:00:00Z")) | 结合时钟使用 |
| Between | between(ZonedDateTime start, ZonedDateTime end) | 匹配时间区间内的请求 | between(now, now.plusMinutes(10)) | 两个时间点之间有效 |
| RemoteAddr | remoteAddr(String... addresses) | 匹配客户端 IP 地址 | remoteAddr("192.168.1.1/24") | 支持 CIDR 格式 |
3.3 使用 YAML 配置路由
| 配置项 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| spring.cloud.gateway.routes.id | id: route1 | 路由唯一标识 | id: product-route | 必须唯一 |
| spring.cloud.gateway.routes.uri | uri: http://localhost:8081 | 目标服务地址 | uri: lb://product-service | lb 表示使用负载均衡 |
| spring.cloud.gateway.routes.predicates | predicates: - Path=/product/** | 断言规则列表 | predicates: - Path=/api/** - Method=GET | 每个断言占一行,用 - 开头 |
| spring.cloud.gateway.routes.filters | filters: - StripPrefix=1 | 过滤器列表 | filters: - AddRequestHeader=X-Trace-ID,12345 | 多个过滤器按顺序执行 |
| spring.cloud.gateway.discovery.locator.enabled | true/false | 是否自动创建路由(基于服务发现) | enabled: true | 开启后自动为注册中心服务创建路由 |
YAML 示例:
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/user/**
- Method=GET
filters:
- StripPrefix=1
- AddRequestHeader=X-Request-From, Gateway
第四章:过滤器工厂(GatewayFilter & GlobalFilter)
4.1 内置 GatewayFilter 工厂方法
GatewayFilter 用于在请求被路由前后修改请求或响应。以下为常用内置过滤器工厂的方法。
| 方法名 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| AddRequestHeader | AddRequestHeader=headerName, headerValue | 添加请求头 | filters: - AddRequestHeader=X-User-ID, 12345 | 支持占位符,如 %{attributes.foo} |
| AddRequestParameter | AddRequestParameter=name, value | 添加查询参数 | filters: - AddRequestParameter=source, gateway | 原请求参数保留,新增参数追加 |
| AddResponseHeader | AddResponseHeader=headerName, value | 添加响应头 | filters: - AddResponseHeader=X-Response-Time, %{timestamp} | 可在响应阶段插入自定义信息 |
| StripPrefix | StripPrefix=n | 剥离前 n 级路径 | filters: - StripPrefix=1 | 如 /api/user → /user 转发到目标服务 |
| PrefixPath | PrefixPath=/prefix | 在路径前添加前缀 | filters: - PrefixPath=/v1 | 所有请求路径前自动加 /v1 |
| SetPath | SetPath=/newpath/{segment} | 重写请求路径 | filters: - SetPath=/users/{segment} | 支持路径变量提取和重写 |
| RewritePath | RewritePath=/oldpath/(?.*), /newpath/${segment} | 重写路径正则匹配部分 | RewritePath=/service/(?.*), /${segment} | 注意转义 $ 为 $\{} |
| SetStatus | SetStatus=401 或 SetStatus=UNAUTHORIZED | 设置响应状态码 | filters: - SetStatus=403 | 可用于拦截并返回固定状态 |
| SetResponseHeader | SetResponseHeader=header, value | 替换响应头(若存在) | filters: - SetResponseHeader=Content-Type, application/json | 若头已存在则覆盖 |
| RemoveRequestHeader | RemoveRequestHeader=headerName | 移除指定请求头 | filters: - RemoveRequestHeader=Cookie | 增强安全性,防止敏感头泄露 |
| RemoveResponseHeader | RemoveResponseHeader=headerName | 移除响应中的指定头 | filters: - RemoveResponseHeader=Server | 隐藏服务器信息 |
| Retry | Retry=3, INTERNAL_ERROR, GATEWAY_TIMEOUT | 请求失败时重试 | filters: - Retry=3, 500, GET | 注意幂等性,避免重复提交 |
4.2 全局过滤器(GlobalFilter)核心方法
GlobalFilter 对所有路由生效,无需配置在具体路由中。
| 方法名 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| GlobalFilter.filter | Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) | 定义全局处理逻辑 | public class AuthFilter implements GlobalFilter { ... } | 必须实现接口并注册为 Bean |
| ServerWebExchange.getAttributes | Object getAttribute(String key) | 获取上下文属性 | String user = exchange.getAttribute("user"); | 可用于跨过滤器传递数据 |
| ServerWebExchange.getRequest | exchange.getRequest() | 获取原始请求对象 | URI uri = exchange.getRequest().getURI(); | 不可变对象,需通过 mutated 构建新请求 |
| ServerWebExchange.getResponse | exchange.getResponse() | 获取响应对象 | HttpStatus status = exchange.getResponse().getStatusCode(); | 可设置状态、头信息等 |
| GatewayFilterChain.filter | chain.filter(exchange) | 继续执行后续过滤器链 | return chain.filter(exchange); | 必须调用否则请求中断 |
| exchange.mutate().request() | 构建新的 ServerHttpRequest | 修改请求头或路径 | .request(req -> req.getHeader("X-Forwarded-For")) | 使用 mutate 模式创建不可变对象副本 |
| ResponseDecorator | decorate the response body | 修改响应体内容 | WebFilter 封装方式实现 | 复杂,需结合 DataBuffer 处理流 |
Java 示例:
@Component
public class LoggingGlobalFilter implements GlobalFilter {
private static final Logger log = LoggerFactory.getLogger(LoggingGlobalFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("Request {} : {}", exchange.getRequest().getMethod(), exchange.getRequest().getURI());
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("Response status: {}", exchange.getResponse().getStatusCode());
}));
}
}
4.3 过滤器的执行顺序与优先级
Spring Cloud Gateway 中过滤器按类型和 order 决定执行顺序。
| 类型 | 执行阶段 | 排序依据 | 示例 | 注意事项 | |: --- | --- | --- | --- | --- | | GlobalFilter | 全局作用域 | 实现 Ordered 接口或 @Order 注解 | 自定义鉴权、日志过滤器 | order 值越小越早执行 | | GatewayFilter | 局部作用域 | 配置顺序或 filter(order) 参数 | StripPrefix, AddHeader | 在 route 配置中顺序决定 | | Pre Filters | 路由前执行 | order 升序 | Authentication, Rate Limiting | 修改请求前的操作 | | Post Filters | 路由后执行 | order 降序 | Logging, Header Stripping | 处理响应内容 | | 默认排序规则 | —— | GlobalFilter < GatewayFilter;Pre < Route < Post | —— | Spring 内部使用 Flux 排序机制 |
说明:
- 所有 Pre 类型过滤器先于路由执行。
- 所有 Post 类型过滤器在路由完成后执行。
- 自定义 GlobalFilter 应实现 Ordered 接口以明确顺序。
示例实现:
@Component
@Order(-1)
public class HighPriorityFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 最早执行的全局过滤器
return chain.filter(exchange);
}
}
第五章:路由发现与动态网关配置
5.1 基于服务发现的自动路由(DiscoveryClient)
通过集成 Eureka、Nacos 等注册中心,实现自动创建路由。
| 方法名 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| spring.cloud.gateway.discovery.locator.enabled | enabled: true | 开启基于服务发现的路由自动创建 | enabled: true | 默认路径为 /serviceId/** |
| spring.cloud.gateway.discovery.locator.lowerCaseServiceId | lowerCaseServiceId: true | 将服务 ID 转为小写用于路径匹配 | lowerCaseServiceId: true | 避免大小写不一致导致 404 |
| spring.cloud.gateway.discovery.locator.includeExpression | includeExpression: true | 自定义包含服务的表达式 | includeExpression: name != 'gateway' | SpEL 表达式,灵活控制 |
| spring.cloud.gateway.discovery.locator.urlExpression | urlExpression: ... | 自定义目标 URL 表达式 | urlExpression: "'http://' + serviceId + ':8080'" | 高级用法,覆盖默认 lb:// |
| spring.cloud.gateway.discovery.locator.routeIdPrefix | routeIdPrefix: api- | 为自动生成的路由添加前缀 | routeIdPrefix: svc- | 便于识别和管理 |
YAML 示例:
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
lowerCaseServiceId: true
routeIdPrefix: svc-
说明:
- 启用后,每个注册的服务将自动生成一条路由规则,路径为
/${serviceId}/**,目标为lb://serviceId。 - 可结合
@LoadBalancerClient实现更复杂的负载均衡策略。
5.2 动态路由配置:通过 Actuator 端点修改路由
使用 spring-cloud-starter-gateway 提供的 /actuator/gateway/routes 端点实现运行时动态增删改查路由。
| 方法名 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| POST /actuator/gateway/routes/{id} | HTTP POST 请求 | 添加或更新指定 ID 的路由 | curl -X POST ... -d '{ "id": "new-route", "uri": "http://example.com", "predicates": [ { "name": "Path", "args": { "pattern": "/test/**" } } ] }' | Content-Type: application/json |
| GET /actuator/gateway/routes | HTTP GET 请求 | 获取所有当前路由配置 | curl http://localhost:8080/actuator/gateway/routes | 返回 JSON 数组 |
| GET /actuator/gateway/routes/{id} | HTTP GET 请求 | 获取指定 ID 的路由详情 | curl http://localhost:8080/actuator/gateway/routes/user-route | 返回单个路由对象 |
| DELETE /actuator/gateway/routes/{id} | HTTP DELETE 请求 | 删除指定 ID 的路由 | curl -X DELETE http://localhost:8080/actuator/gateway/routes/user-route | 删除后立即失效 |
| refresh | POST /actuator/gateway/refresh | 刷新路由缓存(重新加载) | curl -X POST http://localhost:8080/actuator/gateway/refresh | 手动触发刷新,适用于配置变更 |
启用配置:
management:
endpoint:
gateway:
enabled: true
endpoints:
web:
exposure:
include: gateway,refresh
注意事项:
- 生产环境需对
/actuator端点进行安全控制(如 Spring Security)。 - 动态路由不持久化,重启后丢失,需结合配置中心实现持久化。
5.3 自定义 RouteDefinitionLocator 实现动态路由加载
通过实现 RouteDefinitionLocator 接口,从数据库、Redis 或配置中心加载路由配置。
| 方法名 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| RouteDefinitionLocator.getRouteDefinitions | Flux<RouteDefinition> getRouteDefinitions() | 提供路由定义流 | return Flux.fromIterable(loadFromDB()); | 必须返回 Flux 流,支持响应式 |
| RouteDefinition.setId | routeDefinition.setId("xxx") | 设置路由 ID | routeDef.setId("order-service"); | 必须唯一 |
| RouteDefinition.setUri | routeDefinition.setUri(URI uri) | 设置目标地址 | routeDef.setUri(URI.create("lb://order")); | 支持 lb:// 实现负载均衡 |
| RouteDefinition.setPredicates | predicates: List<PredicateDefinition> | 设置断言定义列表 | 见下方代码示例 | 每个 PredicateDefinition 包含 name 和 args |
| RouteDefinition.setFilters | filters: List<FilterDefinition> | 设置过滤器定义列表 | FilterDefinition filter = new FilterDefinition("StripPrefix=1"); | 语法与 YAML 一致 |
Java 示例:
@Component
public class DatabaseRouteDefinitionLocator implements RouteDefinitionLocator {
@Autowired
private RouteConfigService routeConfigService; // 从 DB 加载路由配置
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(routeConfigService.findAll().stream()
.map(config -> {
RouteDefinition definition = new RouteDefinition();
definition.setId(config.getId());
definition.setUri(URI.create(config.getUri()));
// 构建 predicates 和 filters...
return definition;
}).collect(Collectors.toList()));
}
}
说明:
- 结合定时任务或消息监听(如 Redis Pub/Sub),可实现配置变更自动刷新。
- 推荐与 Nacos、Apollo 等配置中心集成,实现集中管理。
第六章:限流、熔断与安全控制
6.1 基于 Redis 的限流过滤器(RequestRateLimiter)
使用 RequestRateLimiterGatewayFilterFactory 实现网关级限流。
| 方法名 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| KeyResolver.resolve | Mono<String> resolve(ServerWebExchange exchange) | 提供限流键(如 IP、用户、URL) | return Mono.just(exchange.getRequest().getRemoteAddress().getHostName()); | 必须注册为 Bean,用于生成限流 key |
| redis-rate-limiter.replenishRate | replenishRate: 10 | 每秒补充的令牌数(平均速率) | replenishRate: 5 | 控制请求的平均处理速度 |
| redis-rate-limiter.burstCapacity | burstCapacity: 20 | 令牌桶总容量(最大突发流量) | burstCapacity: 10 | 超过此值的请求将被拒绝 |
| redis-rate-limiter.requestedTokens | requestedTokens: 1 | 每次请求消耗的令牌数 | requestedTokens: 1 | 默认为 1,可自定义 |
| Filter 配置语法 | name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 10 redis-rate-limiter.burstCapacity: 20 key-resolver: "#{@ipKeyResolver}" | 配置限流规则 | filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 5 redis-rate-limiter.burstCapacity: 10 key-resolver: "#{@userKeyResolver}" | key-resolver 引用 Spring Bean |
YAML 示例:
spring:
cloud:
gateway:
routes:
- id: rate-limited-route
uri: http://localhost:8081
predicates:
- Path=/api/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 5
redis-rate-limiter.burstCapacity: 10
key-resolver: "#{@ipKeyResolver}"
KeyResolver 示例(按 IP 限流):
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
注意事项:
- 需引入
spring-boot-starter-data-redis-reactive依赖。 - Redis 作为令牌桶状态存储,确保高可用。
6.2 集成 Hystrix 实现熔断与降级
使用 HystrixGatewayFilterFactory 提供服务降级能力。
| 方法名 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| fallbackUri | fallbackUri: forward:/fallback | 指定降级时转发的本地 URI | fallbackUri: forward:/service-down | 仅支持 forward 协议 |
| execution.isolation.strategy | 设置隔离策略(SEMAPHORE/THREAD) | 在 Hystrix 配置中设置 | hystrix.command.default.execution.isolation.strategy: SEMAPHORE | 推荐 SEMAPHORE 避免线程切换 |
| circuitBreaker.enabled | circuitBreaker.enabled: true | 开启熔断器 | 默认开启 | 可关闭用于调试 |
| metrics.rollingStats.timeInMilliseconds | 统计窗口时间 | 如 10000(10秒) | 默认 10000ms | 决定滑动窗口大小 |
| circuitBreaker.requestVolumeThreshold | 触发熔断的最小请求数 | 如 20 | 默认 20 | 请求量过低不开启熔断 |
| circuitBreaker.sleepWindowInMilliseconds | 熔断后等待时间 | 如 5000(5秒) | 默认 5000ms | 之后尝试半开状态 |
| circuitBreaker.errorThresholdPercentage | 错误率阈值 | 如 50(50%) | 默认 50% | 超过则熔断 |
YAML 示例:
spring:
cloud:
gateway:
routes:
- id: hystrix-route
uri: lb://slow-service
predicates:
- Path=/slow/**
filters:
- name: Hystrix
args:
name: slowCommand
fallbackUri: forward:/fallback
Fallback 处理:
@RestController
public class FallbackController {
@GetMapping("/fallback")
public Mono<Map<String, Object>> fallback() {
Map<String, Object> result = new HashMap<>();
result.put("success", false);
result.put("message", "Service is unavailable, please try later.");
return Mono.just(result);
}
}
注意事项:
spring-cloud-starter-netflix-hystrix已进入维护模式,建议评估迁移至 Resilience4j。fallbackUri必须为forward:开头,指向网关内部 Controller。
6.3 安全控制:全局鉴权过滤器
通过 GlobalFilter 实现统一身份认证。
| 方法名 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| ServerHttpRequest.getHeaders | exchange.getRequest().getHeaders() | 获取请求头信息 | List<String> auth = headers.get("Authorization"); | 返回不可变列表 |
| ServerHttpResponse.setStatusCode | exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED) | 设置响应状态码 | response.setStatusCode(HttpStatus.FORBIDDEN); | 必须在写入前设置 |
| ServerHttpResponse.setComplete | exchange.getResponse().setComplete() | 终止请求并返回响应 | return exchange.getResponse().setComplete(); | 阻止后续过滤器执行 |
| ServerHttpResponse.writeWith | writeWith(Publisher) | 写入响应体内容 | return response.writeWith(Mono.just(buffer)); | 需手动管理 DataBuffer |
| ServerWebExchange.getFormData | exchange.getFormData() | 获取表单数据(POST) | exchange.getFormData().subscribe(data -> ...); | 适用于 application/x-www-form-urlencoded |
Java 示例(JWT 鉴权):
@Component
@Order(-1)
public class AuthGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String authHeader = request.getHeaders().getFirst("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
String token = authHeader.substring(7);
if (!JwtUtil.validate(token)) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
}
注意事项:
- 鉴权逻辑应尽量轻量,避免阻塞。
- 敏感服务建议结合 OAuth2、JWT 等标准协议。
- 可使用 Spring Security Reactive 进行更复杂的安全控制。
第七章:监控、日志与性能优化
7.1 集成 Micrometer 与监控指标(Actuator Metrics)
Spring Cloud Gateway 内置对 Micrometer 的支持,可暴露丰富的监控指标。
| 方法名 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| spring.boot.actuator.metrics.web.server.auto-time-requests | auto-time-requests: true | 自动记录 HTTP 请求耗时 | 默认开启 | 生成 timer 类型指标 |
| meterRegistry.counter | counter(String name, Iterable tags) | 手动记录事件计数 | registry.counter("gateway.requests.total", "service", "user").increment(); | 用于自定义业务指标 |
| meterRegistry.timer | timer(String name, Iterable tags) | 手动记录耗时 | Timer.Sample sample = Timer.start(registry); ... sample.stop(registry.timer("gateway.auth.duration")); | 精确测量某段逻辑耗时 |
| GatewayMetricsFilter | 自动注册的 GlobalFilter | 收集路由、过滤器执行指标 | 无需手动配置 | 暴露如 spring.cloud.gateway.requests 等指标 |
| GET /actuator/metrics | HTTP GET 请求 | 查询可用指标列表 | curl http://localhost:8080/actuator/metrics | 返回指标名称列表 |
| GET /actuator/metrics/{name} | HTTP GET 请求 | 获取指定指标详情 | curl "http://localhost:8080/actuator/metrics/http.server.requests?tag=uri:/user" | 支持 tag 过滤 |
启用配置:
management:
endpoints:
web:
exposure:
include: metrics,health,info
metrics:
tags:
application: ${spring.application.name}
常用指标:
http.server.requests:HTTP 请求统计(含状态码、URI、方法、耗时)spring.cloud.gateway.requests:网关特有指标,如路由匹配、过滤器执行jvm.memory.used:JVM 内存使用reactor.netty.http.client.connections:Netty 连接池状态
注意事项:
- 结合 Prometheus 抓取指标,使用 Grafana 可视化。
- 高频指标可能影响性能,合理设置采样率。
7.2 日志记录与调试技巧
通过日志分析网关行为,定位问题。
| 方法名 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| LoggingExchangeFilterFunction | 包装其他 Filter | 打印请求/响应头信息 | .filter(new LoggingExchangeFilterFunction()) | 仅用于调试,生产慎用 |
| spring.cloud.gateway.httpclient.wiretap | wiretap: true | 开启 Netty 级别 Wiretap 日志 | httpclient: wiretap: true | 输出极详细,性能损耗大 |
| ServerWebExchange.getAttributes | getAttributes().put(key, value) | 在过滤器间传递上下文信息 | exchange.getAttributes().put("request-start-time", System.currentTimeMillis()); | 用于计算耗时、追踪 ID 等 |
| Logger.debug/info/error | log.debug("msg"), log.info("req: {}", req.getURI()) | 输出自定义日志 | private static final Logger log = LoggerFactory.getLogger(AuthFilter.class); | 建议使用 SLF4J |
| reactor.netty.http.client.HttpClient | doOnConnected, doOnRequest 等 | 监听客户端事件 | HttpClient.create().wiretap(true).doOnConnected(conn -> log.info("Connected")) | 高级调试,需理解 Reactor |
YAML 配置示例:
logging:
level:
org.springframework.cloud.gateway: DEBUG
reactor.netty: DEBUG
org.springframework.http: DEBUG
推荐日志策略:
- 生产环境:INFO 级别,仅记录关键事件(如鉴权失败、熔断触发)
- 调试环境:DEBUG 或 TRACE,开启 wiretap 查看完整请求/响应
- 使用 MDC(Mapped Diagnostic Context)传递 traceId、spanId 实现链路追踪
7.3 性能优化建议与最佳实践
提升网关吞吐量与响应速度。
| 优化项 | 方法 | 说明 | 示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| 启用 GZIP 压缩 | spring.cloud.gateway.httpclient.responseTimeout | 减少网络传输体积 | response-timeout: 10s | 需后端服务支持 |
| 调整线程模型 | reactor.netty.http.server.accessLog | 优化 Reactor 线程数 | accessLog: true | 默认使用 EventLoop,避免阻塞操作 |
| 缓存路由配置 | RouteDefinitionLocator 缓存 | 避免频繁加载路由 | 使用 Caffeine 缓存 DB 查询结果 | 动态刷新时清除缓存 |
| 连接池配置 | spring.cloud.gateway.httpclient.pool | 配置 Netty 连接池 | pool: maxConnections: 500 acquireTimeout: 10000 | 根据后端服务能力调整 |
| 减少过滤器链 | 合并功能相似的过滤器 | 降低处理开销 | 将多个 Header 操作合并 | 过多过滤器增加延迟 |
| 使用 lb:// 协议 | uri: lb://service-name | 利用 Ribbon 或 LoadBalancer | 避免硬编码 IP | 实现服务发现与负载均衡 |
| 避免阻塞调用 | 不在 GlobalFilter 中使用 Thread.sleep() | 保持响应式非阻塞 | 使用 Mono.delay() 替代 | 阻塞会破坏 Reactor 模型 |
性能测试建议:
- 使用 JMeter、Gatling 进行压测,关注 TPS、P99 延迟。
- 监控 GC 频率与内存使用,避免频繁 Full GC。
- 分析线程堆栈,排查潜在阻塞点。
第八章:高级特性与自定义组件
8.1 自定义 GatewayFilterFactory
通过继承 AbstractGatewayFilterFactory 实现自定义过滤器工厂,支持配置化使用。
| 方法名 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| apply | public GatewayFilter apply(Config config) | 创建并返回 GatewayFilter 实例 | return (exchange, chain) -> { ... }; | 核心逻辑,定义过滤行为 |
| shortcutFieldOrder | protected List<String> shortcutFieldOrder() | 定义快捷配置字段顺序 | return Arrays.asList("prefix", "suffix"); | 用于 YAML 简写语法 |
| getConfigClass | public Class<Config> getConfigClass() | 返回配置类类型 | return Config.class; | 用于类型安全配置 |
| name | getClass().getSimpleName().replace("GatewayFilterFactory", "") | 决定过滤器在 YAML 中的名称 | MyCustom → MyCustom | 命名规范:XxxGatewayFilterFactory |
| Config 类定义 | public static class Config { ... } | 封装过滤器参数 | private String prefix; private String suffix; | 提供 getter/setter |
Java 示例(添加前后缀头):
@Component
public class AddPrefixSuffixHeaderGatewayFilterFactory
extends AbstractGatewayFilterFactory<AddPrefixSuffixHeaderGatewayFilterFactory.Config> {
public AddPrefixSuffixHeaderGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest().mutate()
.header("X-Custom", config.prefix + "-value-" + config.suffix)
.build();
return chain.filter(exchange.mutate().request(request).build());
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("prefix", "suffix");
}
public static class Config {
private String prefix = "pre";
private String suffix = "suf";
// getter and setter
public String getPrefix() { return prefix; }
public void setPrefix(String prefix) { this.prefix = prefix; }
public String getSuffix() { return suffix; }
public void setSuffix(String suffix) { this.suffix = suffix; }
}
}
YAML 使用:
filters:
- AddPrefixSuffixHeader=begin, end # 快捷语法
# 或完整语法:
- name: AddPrefixSuffixHeader
args:
prefix: start
suffix: finish
注意事项:
- 类名必须以
GatewayFilterFactory结尾。 - 注册为 Spring Bean 才能被自动发现。
- 配置类应设计为不可变或线程安全。
8.2 自定义 GlobalFilter
实现 GlobalFilter 和 Ordered 接口,全局拦截所有请求。
| 方法名 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| filter | Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) | 定义全局处理逻辑 | return chain.filter(exchange); | 必须调用 chain.filter() 继续执行 |
| getOrder | int getOrder() | 定义执行优先级 | return -1; | 越小越早执行 |
| ServerWebExchange.getFormData | exchange.getFormData() | 获取 POST 表单数据 | exchange.getFormData().subscribe(data -> log.info("Form: {}", data)); | 仅适用于 application/x-www-form-urlencoded |
| ServerWebExchange.getSession | exchange.getSession() | 获取会话(WebSession) | exchange.getSession().doOnSuccess(session -> session.getAttributes().put("user", "id123")) | 响应式会话操作 |
| ServerHttpRequestDecorator | 包装原始请求 | 修改请求体(Body) | new ServerHttpRequestDecorator(req) { ... } | 请求体修改复杂,需重写 getBody() |
Java 示例(请求计数):
@Component
@Order(-2)
public class RequestCounterGlobalFilter implements GlobalFilter, Ordered {
private final AtomicInteger counter = new AtomicInteger(0);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
int count = counter.incrementAndGet();
log.info("Total requests processed: {}", count);
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -2; // 高优先级
}
}
注意事项:
- 避免在 filter 方法中执行阻塞操作。
- 使用 @Order 或实现 Ordered 接口明确顺序。
- 可结合 Reactor Context 传递跨阶段数据。
8.3 自定义 PredicateFactory
继承 AbstractRoutePredicateFactory 实现自定义路由匹配逻辑。
| 方法名 | 语法 | 用途 | 代码示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| apply | public Predicate<ServerWebExchange> apply(Config config) | 返回断言逻辑 | return exchange -> config.getValue().equals("enabled"); | 决定是否匹配该路由 |
| getConfigClass | public Class<Config> getConfigClass() | 返回配置类类型 | return Config.class; | 支持类型安全配置 |
| shortcutFieldOrder | protected List<String> shortcutFieldOrder() | 快捷参数顺序 | return Collections.singletonList("value"); | 支持 YAML 简写 |
| name | getClass().getSimpleName().replace("RoutePredicateFactory", "") | 决定在 YAML 中的名称 | Custom → Custom | 命名规范:XxxRoutePredicateFactory |
Java 示例(基于请求头值匹配):
@Component
public class HeaderValueRoutePredicateFactory
extends AbstractRoutePredicateFactory<HeaderValueRoutePredicateFactory.Config> {
public HeaderValueRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
String header = exchange.getRequest().getHeaders().getFirst(config.headerName);
return header != null && header.equals(config.expectedValue);
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("headerName", "expectedValue");
}
public static class Config {
private String headerName;
private String expectedValue;
// getter and setter
public String getHeaderName() { return headerName; }
public void setHeaderName(String headerName) { this.headerName = headerName; }
public String getExpectedValue() { return expectedValue; }
public void setExpectedValue(String expectedValue) { this.expectedValue = expectedValue; }
}
}
YAML 使用:
predicates:
- HeaderValue=X-Feature, enabled
注意事项:
- 断言应尽量轻量,避免复杂计算。
- 可结合缓存提升高频匹配性能。
- 断言失败时,请求将尝试匹配下一个路由。
第九章:实战案例与架构设计
9.1 多租户网关路由设计
实现基于租户标识(如域名、Header、路径前缀)的动态路由。
| 方法 | 实现方式 | 用途 | 代码/配置示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| 基于 Host 路由 | 使用 HostRoutePredicateFactory | 不同域名指向不同服务 | predicates: - Host=*.{tenant}.example.com - Path=/** | 配合 DNS 实现多租户隔离 |
| 基于 Header 识别租户 | 自定义 GlobalFilter 提取 tenant-id | 统一通过 Header 传递租户信息 | String tenant = request.getHeaders().getFirst("X-Tenant-ID"); | 需在后续服务中透传 |
| 动态 URI 构建 | 在过滤器中修改 uri | 根据租户选择不同集群 | uri: lb://tenant-service-${tenant} | 需结合 ServiceInstanceListSupplier |
| 路径前缀区分 | 使用 Path 断言 + StripPrefix | /{tenant}/api → 剥离后转发 | predicates: - Path=/{tenant}/api/** filters: - StripPrefix=2 | 前缀需在路由定义中提取 |
| 租户级限流 | KeyResolver 按 tenant 分流 | 不同租户独立限流策略 | KeyResolver: "#{@tenantKeyResolver}" | 结合 Redis 实现分布式计数 |
Java 示例(Host 解析租户):
@Component
public class TenantHostResolverFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String host = exchange.getRequest().getHeaders().getHost().hostString();
// 解析 tenant.example.com → tenant
String[] parts = host.split("\\.");
if (parts.length > 2) {
String tenant = parts[0];
exchange.getAttributes().put("tenant", tenant);
}
return chain.filter(exchange);
}
}
YAML 路由示例:
- id: tenant-route
uri: lb://${exchange.attributes.tenant}-service
predicates:
- Host=**.example.com
- Path=/**
filters:
- StripPrefix=1
注意事项:
- 租户信息应在网关层统一注入上下文,避免下游服务解析。
- 数据隔离需在服务层配合实现。
- 考虑租户配置的动态加载与缓存。
9.2 灰度发布与金丝雀部署
通过网关实现新版本服务的渐进式流量切分。
| 方法 | 实现方式 | 用途 | 代码/配置示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| 基于 Header 路由 | Predicate 匹配特定 Header | 内部测试人员访问新版本 | predicates: - Header=X-Release, canary | 精准控制,适合小范围测试 |
| 基于用户 ID 路由 | 自定义 Predicate 解析用户 | 指定用户群体灰度 | if (userId % 100 < 10) routeToV2(); | 需用户信息,适合登录场景 |
| 基于权重分流 | 使用 LoadBalancer 的权重策略 | 按百分比分配流量 | nacos: metadata: weight: 90 | 结合 Nacos 权重配置 |
| 时间窗口灰度 | 使用 After/Before 断言 | 特定时间段内启用新版本 | predicates: - After=2025-01-01T00:00:00Z | 适合定时上线 |
| 动态路由切换 | 通过 Actuator 动态更新路由 | 运行时调整路由目标 | POST /actuator/gateway/routes/canary-route | 需配合配置中心 |
YAML 示例(Header 控制):
- id: canary-route
uri: lb://user-service-v2
predicates:
- Header=X-Canary, true
- Path=/user/**
filters:
- AddResponseHeader=X-Service-Version, v2
- id: stable-route
uri: lb://user-service-v1
predicates:
- Path=/user/**
filters:
- AddResponseHeader=X-Service-Version, v1
Java 示例(用户 ID 哈希):
public class UserIdCanaryPredicateFactory
extends AbstractRoutePredicateFactory<UserIdCanaryPredicateFactory.Config> {
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
String uid = exchange.getRequest().getHeaders().getFirst("X-User-ID");
if (uid == null) return false;
int hash = uid.hashCode() & Integer.MAX_VALUE;
return hash % 100 < config.percentage; // 百分比灰度
};
}
public static class Config {
private int percentage = 10; // 默认 10%
// getter/setter
}
}
注意事项:
- 灰度期间需加强监控与日志对比。
- 避免会话不一致(如登录状态)。
- 准备快速回滚方案。
9.3 API 网关与微前端集成
将网关作为微前端架构的统一入口,聚合后端服务。
| 方法 | 实现方式 | 用途 | 代码/配置示例 | 注意事项 |
|: --- | --- | --- | --- | --- |
| 静态资源代理 | 路由到前端资源服务器 | / → index.html, /js/* → js/ | - Path=/, /js/**, /css/** - RewritePath=/, /index.html | 支持 SPA 路由 |
| 后端服务聚合 | 多路由规则指向不同微服务 | /api/user → user-service /api/order → order-service | uri: lb://user-service | 统一 API 前缀 |
| 请求头统一处理 | GlobalFilter 添加 CORS、安全头 | 允许跨域、X-Frame-Options | response.getHeaders().add("Access-Control-Allow-Origin", "*"); | 生产环境限制 Origin |
| 路径重写 | RewritePath 过滤器 | /api/user/1 → /user/1 | RewritePath=/api/(?.*), /${segment} | 隐藏内部结构 |
| 认证统一入口 | 鉴权 Filter 拦截所有 API | JWT 验证、OAuth2 | 拒绝不合法请求 | 静态资源可放行 |
YAML 配置示例:
spring:
cloud:
gateway:
routes:
# 前端静态资源
- id: frontend
uri: http://static-server
predicates:
- Path=/, /js/**, /css/**, /img/**
filters:
- Name: RewritePath
args:
regexp: /
replacement: /index.html
# 用户服务
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
- StripPrefix=2
# 订单服务
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/order/**
filters:
- StripPrefix=2
注意事项:
- 静态资源建议由 CDN 托管,减轻网关压力。
- 微前端路由与后端 API 路由清晰分离。
- 考虑前端资源版本化(如
/v1/index.html)避免缓存问题。
第十章:常见问题排查与生产最佳实践
10.1 常见错误与解决方案
针对生产环境中高频出现的问题提供诊断与修复方案。
| 错误现象 | 可能原因 | 诊断方法 | 解决方案 | 预防措施 | |: --- | --- | --- | --- | --- | | 404 Not Found | 路由未匹配、路径未剥离 | 检查 /actuator/gateway/routes 输出 | 核对 Path 断言与 StripPrefix 配置 | 使用 curl -v 测试路径匹配 | | 500 Internal Error | 后端服务不可达、过滤器异常 | 查看网关日志 ERROR 级别 | 检查服务注册状态、过滤器代码逻辑 | 启用 Hystrix 降级 | | 503 Service Unavailable | 服务实例未注册、健康检查失败 | 检查 Eureka/Nacos 控制台 | 确认服务启动并注册成功 | 配置合理的健康检查路径 | | 请求超时 | 后端响应慢、连接池耗尽 | 启用 wiretap: true 查看 Netty 日志 | 调整 response-timeout、扩大连接池 | 设置合理的超时与熔断策略 | | 内存溢出 (OOM) | 请求体过大、数据缓冲未释放 | 分析堆转储 (Heap Dump) | 限制 max-in-memory-size、避免大文件上传 | 配置请求大小限制 | | 路由不生效 | 配置未加载、YAML 缩进错误 | 检查 application.yml 语法 | 使用在线 YAML 验证器校验 | 启用 @ConfigurationProperties 验证 | | 限流失效 | Redis 连接失败、KeyResolver 返回 null | 检查 Redis 状态、日志输出 | 确保 Redis 可用、KeyResolver 返回有效值 | 添加 Redis 健康检查 | | 循环重定向 | 路由配置错误导致自循环 | 使用 curl -v 观察 Location 头 | 检查 RedirectTo 过滤器目标地址 | 避免路由规则覆盖自身路径 |
诊断工具推荐:
curl -v http://gateway/path:查看详细请求/响应头http://localhost:8080/actuator/gateway/routes:验证运行时路由配置http://localhost:8080/actuator/health:检查网关健康状态jstack <pid>:分析线程阻塞jmap -histo <pid>:查看内存对象分布
10.2 生产环境部署最佳实践
确保网关在生产环境中的高可用、安全与可维护性。
| 实践领域 | 最佳实践 | 说明 | 实施建议 | |: --- | --- | --- | --- | | 高可用部署 | 集群部署 + 负载均衡 | 避免单点故障 | 使用 Nginx/LVS 做前置负载,网关实例 ≥ 2 | | 配置管理 | 集成 Nacos/Apollo 配置中心 | 实现配置动态化、版本化 | 路由、限流规则存于配置中心,监听变更 | | 安全加固 | 启用 HTTPS、IP 白名单 | 防止未授权访问 | 使用 Let’s Encrypt 证书,关键端点加 IP 限制 | | 监控告警 | 接入 Prometheus + Grafana + AlertManager | 实时监控核心指标 | 告警规则:5xx 错误率 > 1%、P99 延迟 > 1s | | 日志规范 | 结构化日志 + ELK 收集 | 便于搜索与分析 | 使用 JSON 格式,包含 traceId、requestId | | 版本控制 | 路由配置纳入 Git 管理 | 实现变更追溯 | 配置中心配置也需备份到代码仓库 | | 灰度发布 | 先灰度后全量 | 降低变更风险 | 新路由先对内部流量开放,验证无误再全量 | | 资源隔离 | 关键服务独立路由与限流 | 避免故障扩散 | 核心 API 设置独立限流阈值 | | 定期演练 | 故障注入与熔断测试 | 验证系统韧性 | 模拟服务宕机,观察降级是否生效 |
生产配置示例:
server:
port: 8080
ssl:
enabled: true
key-store: classpath:gateway.p12
key-store-password: secret
key-store-type: PKCS12
spring:
cloud:
gateway:
httpclient:
connect-timeout: 1000
response-timeout: 5s
pool:
max-connections: 1000
acquire-timeout: 10000
loadbalancer:
use443: true
management:
endpoints:
web:
exposure:
include: health,info,metrics,gateway
endpoint:
health:
show-details: never # 生产隐藏详情
10.3 性能调优与容量规划
提升网关吞吐量,合理规划资源。
| 优化项 | 调优策略 | 目标 | 工具/方法 |
|: --- | --- | --- | --- |
| JVM 参数 | 合理设置堆大小、GC 策略 | 减少 GC 停顿 | -Xms2g -Xmx2g -XX:+UseG1GC |
| Netty 线程 | 调整 EventLoop 线程数 | 充分利用 CPU | reactor.netty.ioWorkerCount=16 |
| 连接池 | 预热连接、设置合理最大值 | 减少连接创建开销 | max-connections: 1000, lease-timeout: 60s |
| 缓存策略 | 缓存路由定义、鉴权结果 | 降低后端依赖 | 使用 Caffeine 缓存,TTL 30s |
| 请求大小限制 | 设置 max-in-memory-size | 防止 OOM | spring.codec.max-in-memory-size=1MB |
| 压力测试 | 模拟真实流量场景 | 验证系统瓶颈 | 使用 JMeter/Gatling 压测,目标 TPS 5000+ |
| 横向扩展 | 增加网关实例数量 | 提升整体吞吐 | K8s HPA 基于 CPU/TPS 自动扩缩容 |
| CDN 卸载 | 静态资源交由 CDN 处理 | 降低网关负载 | 将 /static/、/assets/ 指向 CDN |
容量评估公式:
所需网关实例数 = (日请求总量 × 峰值系数) / (单实例 QPS × 86400)
- 假设日请求 1 亿,峰值系数 10,单实例 QPS 1000:
- 实例数 = (1e8 × 10) / (1000 × 86400) ≈ 11.5 → 至少 12 个实例
结语: Spring Cloud Gateway 作为微服务架构的流量入口,其稳定性与性能至关重要。通过本目录的系统学习,从基础配置到高级定制,再到生产实践,开发者可全面掌握网关的核心能力。建议在实际项目中遵循”小步快跑、持续验证”的原则,逐步应用各项功能,确保架构稳健演进。