Spring Cloud Gateway 作为 Spring Cloud 生态中的第二代网关组件,其核心架构设计体现了响应式编程的优势。与传统的 Servlet 容器不同,它基于 Netty 和 Project Reactor 构建,采用非阻塞 I/O 模型,这使得它在高并发场景下具有更好的性能表现。
关键设计决策:选择 WebFlux 而非传统 Servlet 容器,是为了应对微服务架构中高频的 API 调用场景。实测表明,在同等硬件条件下,Gateway 的吞吐量可达 Zuul 1.x 的 1.5 倍。
当请求到达 Gateway 时,会经历以下处理链条:

典型的路由配置包含四个核心要素:
yaml复制spring:
cloud:
gateway:
routes:
- id: service_route # 路由唯一标识
uri: lb://service-provider # 目标服务地址
predicates: # 断言条件
- Path=/api/v1/**
filters: # 过滤器链
- StripPrefix=1
| 类型 | 示例 | 适用场景 |
|---|---|---|
| 直接URL | http://service:8080 |
固定地址服务 |
| 注册中心 | lb://service-name |
动态服务发现 |
| 本地跳转 | forward:/local |
网关内部端点 |
Path 断言支持多种匹配模式:
yaml复制predicates:
- Path=/api/{segment} # 单级路径变量
- Path=/api/** # 多级通配路径
- Path=/v1/**,/v2/** # 多路径匹配
路径匹配的底层实现采用 AntPathMatcher,注意
**和*的区别:
*匹配单级路径**匹配任意多级路径
xml复制<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
配置自动路由发现:
yaml复制spring:
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true # 启用小写服务名
必须添加负载均衡依赖:
xml复制<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
命名空间配置示例:
yaml复制spring:
cloud:
nacos:
discovery:
namespace: 8b15a8a7-8237-4ff6-a28e-ac949e399a1f
group: THOMAS_GROUP
| 断言类型 | 配置示例 | 匹配条件 |
|---|---|---|
| Path | - Path=/api/** |
请求路径 |
| Query | - Query=name,tom.* |
查询参数 |
| Method | - Method=GET,POST |
HTTP方法 |
| Header | - Header=X-Request-Id,\d+ |
请求头 |
实现 RoutePredicateFactory 接口:
java复制public class CustomPredicateFactory extends AbstractRoutePredicateFactory<CustomPredicateFactory.Config> {
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
// 自定义判断逻辑
return config.getValue().equals(exchange.getRequest().getHeaders().getFirst(config.getHeaderName()));
};
}
}
code复制Global Pre-Filters → Route Pre-Filters → Proxy Request → Route Post-Filters → Global Post-Filters
| 过滤器 | 作用 | 示例 |
|---|---|---|
| AddRequestHeader | 添加请求头 | - AddRequestHeader=X-Request-Red, Blue |
| RewritePath | 路径重写 | - RewritePath=/old/(?<segment>.*), /new/$\{segment} |
| Retry | 请求重试 | - Retry=3,INTERNAL_SERVER_ERROR |
java复制@Component
@Order(-1)
public class AuthFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if(!validateToken(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
}
java复制@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("https://domain.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
yaml复制spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "https://domain.com"
allowedMethods:
- GET
- POST
allowCredentials: true
yaml复制server:
netty:
max-initial-line-length: 8192 # 最大HTTP头行长度
max-header-size: 32KB # 最大HTTP头大小
spring:
cloud:
gateway:
httpclient:
connect-timeout: 1000 # 连接超时(ms)
response-timeout: 5s # 响应超时
集成 Resilience4j:
java复制@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("circuitbreaker_route", r -> r.path("/api/**")
.filters(f -> f.circuitBreaker(c -> c.setName("myCircuitBreaker")))
.uri("lb://backend-service"))
.build();
}
启用 Actuator 端点:
yaml复制management:
endpoints:
web:
exposure:
include: health,gateway
现象:返回 404 但后端服务正常
检查点:
现象:过滤器执行顺序不符合预期
解决方案:
@Order 注解Ordered.LOWEST_PRECEDENCE ~ Ordered.HIGHEST_PRECEDENCE典型错误:
java复制// 错误示例:阻塞调用
String result = webClient.get().retrieve().bodyToMono(String.class).block();
正确写法:
java复制return webClient.get()
.retrieve()
.bodyToMono(String.class)
.flatMap(result -> {
// 响应式处理
});
通过 RouteDefinitionLocator 接口:
java复制@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
public void addRoute(String id, String path, String uri) {
RouteDefinition definition = new RouteDefinition();
definition.setId(id);
definition.setPredicates(List.of(
new PredicateDefinition("Path=" + path)));
definition.setUri(URI.create(uri));
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
}
基于 Header 的流量路由:
yaml复制spring:
cloud:
gateway:
routes:
- id: canary_route
uri: lb://service-canary
predicates:
- Header=X-Canary, true
- id: stable_route
uri: lb://service-stable
predicates:
- Path=/service/**
使用 ModifyRequestBody 过滤器:
java复制@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewrite_route", r -> r.path("/api/**")
.filters(f -> f.modifyRequestBody(
String.class, String.class,
(exchange, body) -> Mono.just(body.toUpperCase())))
.uri("lb://backend-service"))
.build();
}
java复制public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
try {
Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token);
return chain.filter(exchange);
} catch (Exception e) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}
使用 RedisRateLimiter:
yaml复制filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@remoteAddrKeyResolver}"
java复制public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String ip = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
if(blackList.contains(ip)) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
yaml复制spring:
cloud:
gateway:
httpclient:
pool:
max-connections: 1000 # 最大连接数
acquire-timeout: 5000 # 获取连接超时(ms)
使用 CacheRequestBody 过滤器:
java复制filters:
- name: CacheRequestBody
args:
bodyClass: java.lang.String
java复制public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange)
.doOnSubscribe(s -> log.info("Request started: {}", exchange.getRequest().getURI()))
.doOnTerminate(() -> log.info("Request completed"));
}
java复制@SpringBootTest
class RouteConfigTest {
@Autowired
private WebTestClient webClient;
@Test
void testApiRoute() {
webClient.get()
.uri("/api/test")
.exchange()
.expectStatus().isOk();
}
}
使用 WireMock:
java复制@AutoConfigureWireMock(port = 0)
class GatewayTest {
@Test
void testBackendRoute() {
stubFor(get(urlEqualTo("/backend"))
.willReturn(aResponse()
.withStatus(200)
.withBody("response")));
webClient.get()
.uri("/api/backend")
.exchange()
.expectBody(String.class).isEqualTo("response");
}
}
| Gateway 版本 | Spring Boot 版本 | Spring Cloud 版本 |
|---|---|---|
| 3.1.x | 2.6.x - 2.7.x | 2021.0.x |
| 4.0.x | 3.0.x | 2022.0.x |
java复制public class CustomFilterFactory extends AbstractGatewayFilterFactory<CustomFilterFactory.Config> {
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
// 前置处理
return chain.filter(exchange)
.then(Mono.fromRunnable(() -> {
// 后置处理
}));
};
}
}
java复制@EventListener
public void onRefresh(RefreshScopeRefreshedEvent event) {
// 处理配置更新
}
SmartLifecycle 处理停机请求| 并发数 | 平均响应时间 | 吞吐量 (req/s) | 错误率 |
|---|---|---|---|
| 100 | 62ms | 1,580 | 0% |
| 500 | 78ms | 6,320 | 0% |
| 1000 | 112ms | 8,910 | 0.2% |
yaml复制logging:
level:
org.springframework.cloud.gateway: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
yaml复制management:
metrics:
export:
prometheus:
enabled: true
web:
server:
request:
autotime:
enabled: true
xml复制<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
java复制public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
Span span = tracer.currentSpan();
if (span != null) {
span.tag("api.version", exchange.getRequest().getHeaders().getFirst("X-API-Version"));
}
return chain.filter(exchange);
}
yaml复制spring:
profiles:
active: ${ENV:dev}
---
spring:
config:
activate:
on-profile: prod
cloud:
gateway:
routes:
- id: prod_route
uri: lb://prod-service
使用 Jasypt:
yaml复制spring:
cloud:
gateway:
routes:
- id: secure_route
uri: lb://secure-service
predicates:
- Path=/secure/**
filters:
- AddRequestHeader=Authorization, ENC(加密字符串)
yaml复制filters:
- name: CircuitBreaker
args:
name: backendCircuitBreaker
fallbackUri: forward:/fallback
yaml复制filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY,INTERNAL_SERVER_ERROR
methods: GET,POST
java复制@Bean
public RouteLocator grpcRoute(RouteLocatorBuilder builder) {
return builder.routes()
.route("grpc_route", r -> r.path("/grpc/**")
.filters(f -> f.setPath("/grpc.Greeter/SayHello"))
.uri("grpc://grpc-server:9090"))
.build();
}
yaml复制spring:
cloud:
gateway:
routes:
- id: websocket_route
uri: lb:ws://chat-service
predicates:
- Path=/ws/**
java复制filters:
- name: ModifyRequestBody
args:
inClass: String
outClass: String
rewriteFunction: |
(exchange, body) -> {
JsonNode node = mapper.readTree(body);
((ObjectNode)node).put("timestamp", System.currentTimeMillis());
return Mono.just(mapper.writeValueAsString(node));
}
java复制public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange)
.then(Mono.fromRunnable(() -> {
exchange.getResponse().getHeaders()
.add("X-Response-Time", System.currentTimeMillis() + "");
}));
}
yaml复制spring:
webflux:
max-in-memory-size: 10MB # 内存缓冲区大小
max-request-size: 50MB # 最大请求大小
yaml复制routes:
- id: upload_route
uri: lb://file-service
predicates:
- Path=/upload/**
filters:
- RewritePath=/upload/(?<path>.*), /$\{path}
java复制public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String lang = exchange.getRequest().getHeaders()
.getFirst(HttpHeaders.ACCEPT_LANGUAGE);
exchange.getAttributes().put("lang", lang);
return chain.filter(exchange);
}
java复制@Bean
public ErrorWebExceptionHandler errorHandler() {
return (exchange, ex) -> {
String lang = exchange.getAttribute("lang");
String message = messageSource.getMessage(ex.getClass().getSimpleName(),
null, Locale.forLanguageTag(lang));
exchange.getResponse().writeWith(Mono.just(exchange.getResponse()
.bufferFactory().wrap(message.getBytes())));
return Mono.empty();
};
}
| 指标名称 | 类型 | 说明 |
|---|---|---|
| gateway.requests | Counter | 总请求数 |
| gateway.errors | Counter | 错误请求数 |
| gateway.duration | Timer | 请求处理耗时 |
java复制@Bean
public MeterRegistryCustomizer<MeterRegistry> metrics() {
return registry -> {
registry.config().commonTags("application", "api-gateway");
Gauge.builder("gateway.routes.count",
() -> gatewayProperties.getRoutes().size())
.register(registry);
};
}
java复制public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
HttpHeaders headers = exchange.getRequest().getHeaders();
if (headers.containsKey("X-Sensitive-Header")) {
headers.remove("X-Sensitive-Header");
}
return chain.filter(exchange);
}
java复制public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
return chain.filter(exchange)
.then(Mono.defer(() -> {
String body = exchange.getResponse().getBody().toString();
String masked = body.replaceAll("(\"password\":\")(.*?)(\")", "$1***$3");
return exchange.getResponse().writeWith(
Mono.just(bufferFactory.wrap(masked.getBytes())));
}));
}
yaml复制routes:
- id: canary_route
uri: lb://service-canary
predicates:
- Weight=service-group, 10
- id: stable_route
uri: lb://service-stable
predicates:
- Weight=service-group, 90
java复制public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String userId = getUserId(exchange);
if (isCanaryUser(userId)) {
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR,
URI.create("lb://service-canary"));
}
return chain.filter(exchange);
}
yaml复制spring:
cloud:
gateway:
httpclient:
proxy:
host: istio-ingressgateway.istio-system.svc
port: 80
yaml复制management:
tracing:
sampling:
probability: 1.0
baggage:
correlation-fields: [user-id, session-id]
java复制@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
@AutoConfigureWireMock(port = 0)
class ContractTest {
@Test
void shouldRouteToService() {
stubFor(get(urlEqualTo("/service"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withBody("{\"status\":\"OK\"}")));
webClient.get()
.uri("/api/service")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.status").isEqualTo("OK");
}
}
使用 Gatling:
scala复制class GatewaySimulation extends Simulation {
val httpProtocol = http.baseUrl("http://localhost:8080")
val scn = scenario("API Gateway Test")
.exec(http("request_1")
.get("/api/test")
.check(status.is(200)))
setUp(scn.inject(constantUsersPerSec(100) during(30 seconds)))
.protocols(httpProtocol)
}
在实际项目中使用 Gateway 时,建议从简单路由配置开始,逐步引入断言、过滤器等高级功能。对于关键业务路由,务必配置熔断和监控策略。遇到性能问题时,可优先检查过滤器链的复杂度和响应式编程的正确性。