Spring Cloud Gateway 网关详解
一、知识概述
API 网关是微服务架构中的重要组件,作为系统的统一入口,负责请求路由、协议转换、安全认证、限流熔断等功能。Spring Cloud Gateway 是 Spring Cloud 官方推出的新一代 API 网关,基于 Spring WebFlux 实现,具有高性能、功能强大等特点。
网关的核心功能:
- 路由转发:将请求转发到后端服务
- 负载均衡:分发请求到多个实例
- 认证鉴权:统一的安全控制
- 限流熔断:保护后端服务
- 日志监控:请求追踪和监控
理解网关的原理和配置,是构建微服务系统的关键技能。
二、知识点详细讲解
2.1 网关架构
客户端
│
▼
┌─────────────────────────────────────┐
│ Spring Cloud Gateway │
│ ┌─────────────────────────────────┐ │
│ │ Handler Mapping │ │
│ └─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────┐ │
│ │ Web Handler │ │
│ │ ┌───────────────────────────┐ │ │
│ │ │ Filter Chain │ │ │
│ │ │ ┌─────────────────────┐ │ │ │
│ │ │ │ Pre Filters │ │ │ │
│ │ │ ├─────────────────────┤ │ │ │
│ │ │ │ Route Handler │ │ │ │
│ │ │ ├─────────────────────┤ │ │ │
│ │ │ │ Post Filters │ │ │ │
│ │ │ └─────────────────────┘ │ │ │
│ │ └───────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────┘
│
▼
后端服务
2.2 核心概念
Route(路由)
网关的基本构建块,包含:
- id:路由标识
- uri:目标地址
- predicates:断言条件
- filters:过滤器
Predicate(断言)
匹配 HTTP 请求的条件:
- Path:路径匹配
- Method:方法匹配
- Header:头匹配
- Query:参数匹配
- Time:时间匹配
Filter(过滤器)
处理请求和响应:
- Pre Filter:请求前处理
- Post Filter:响应后处理
2.3 工作流程
1. Handler Mapping 根据 Predicate 匹配路由
↓
2. 创建 Filter Chain
↓
3. 执行 Pre Filters
↓
4. 转发请求到后端服务
↓
5. 执行 Post Filters
↓
6. 返回响应给客户端
2.4 内置断言工厂
| 断言 | 说明 | 示例 |
|---|---|---|
| Path | 路径匹配 | Path=/api/user/** |
| Method | 方法匹配 | Method=GET |
| Header | 请求头匹配 | Header=X-Request-Id |
| Query | 参数匹配 | Query=token |
| Cookie | Cookie 匹配 | Cookie=session |
| After | 时间之后 | After=2024-01-01T00:00:00+08:00 |
| Before | 时间之前 | Before=2024-12-31T23:59:59+08:00 |
| Between | 时间之间 | Between=...,... |
三、代码示例
3.1 基础路由配置
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
# application.yml
server:
port: 8080
spring:
application:
name: gateway-service
cloud:
gateway:
routes:
# 用户服务路由
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
# 订单服务路由
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1
# 商品服务路由
- id: product-service
uri: lb://product-service
predicates:
- Path=/api/products/**
filters:
- StripPrefix=1
3.2 路由配置类
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRoutes(RouteLocatorBuilder builder) {
return builder.routes()
// 用户服务
.route("user-service", r -> r
.path("/api/users/**")
.filters(f -> f
.stripPrefix(1)
.addRequestHeader("X-Gateway", "gateway-service")
.addResponseHeader("X-Response-Time",
String.valueOf(System.currentTimeMillis()))
)
.uri("lb://user-service")
)
// 订单服务
.route("order-service", r -> r
.path("/api/orders/**")
.and()
.method("GET", "POST")
.filters(f -> f
.stripPrefix(1)
.requestRateLimiter(c -> c
.setRateLimiter(userRateLimiter())
)
)
.uri("lb://order-service")
)
// 认证服务
.route("auth-service", r -> r
.path("/api/auth/**")
.filters(f -> f
.stripPrefix(1)
.retry(3)
)
.uri("lb://auth-service")
)
.build();
}
}
3.3 断言配置
spring:
cloud:
gateway:
routes:
# 多条件组合
- id: complex-route
uri: lb://user-service
predicates:
# 路径匹配
- Path=/api/users/**
# 方法匹配
- Method=GET,POST
# 请求头匹配
- Header=X-Request-Id, \d+
# 参数匹配
- Query=token
# Cookie 匹配
- Cookie=session, .+
# 时间匹配
- After=2024-01-01T00:00:00+08:00
# 远程地址匹配
- RemoteAddr=192.168.1.1/24
# 权重路由(灰度发布)
- id: user-service-v1
uri: lb://user-service-v1
predicates:
- Path=/api/users/**
- Weight=group1, 80
- id: user-service-v2
uri: lb://user-service-v2
predicates:
- Path=/api/users/**
- Weight=group1, 20
3.4 内置过滤器
spring:
cloud:
gateway:
routes:
- id: filter-example
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
# 路径处理
- StripPrefix=1 # 去除前缀
- PrefixPath=/api # 添加前缀
- RewritePath=/api/(?<segment>.*), /$\{segment} # 重写路径
# 请求头处理
- AddRequestHeader=X-Request-Foo, Bar
- AddRequestParameter=foo, bar
- RemoveRequestHeader=X-Request-Foo
# 响应头处理
- AddResponseHeader=X-Response-Foo, Bar
- RemoveResponseHeader=X-Response-Foo
- SetResponseHeader=X-Response-Foo, Bar-New
# 重试
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY,SERVICE_UNAVAILABLE
methods: GET
backoff:
firstBackoff: 100ms
maxBackoff: 500ms
factor: 2
# 限流
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@userKeyResolver}"
# 熔断
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/fallback
# 超时
- name: RequestSize
args:
maxSize: 10MB
3.5 自定义过滤器
import org.springframework.cloud.gateway.filter.*;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
// 自定义全局过滤器
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
private static final Logger log = LoggerFactory.getLogger(AuthGlobalFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取请求头中的 Token
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
// 验证 Token
if (!validateToken(token)) {
// 返回 401 未授权
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 解析用户信息并传递
String userId = parseUserId(token);
ServerHttpRequest request = exchange.getRequest().mutate()
.header("X-User-Id", userId)
.build();
return chain.filter(exchange.mutate().request(request).build());
}
@Override
public int getOrder() {
return -100; // 高优先级
}
private boolean validateToken(String token) {
// Token 验证逻辑
return token != null && token.startsWith("Bearer ");
}
private String parseUserId(String token) {
// 解析用户 ID
return "user-123";
}
}
// 自定义网关过滤器
@Component
public class RequestLogFilter implements GlobalFilter, Ordered {
private static final Logger log = LoggerFactory.getLogger(RequestLogFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
long startTime = System.currentTimeMillis();
// 请求日志
log.info("请求开始: {} {} from {}",
request.getMethod(),
request.getPath(),
request.getRemoteAddress());
// 添加开始时间到属性
exchange.getAttributes().put("startTime", startTime);
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
Long startTimeAttr = exchange.getAttribute("startTime");
if (startTimeAttr != null) {
long duration = System.currentTimeMillis() - startTimeAttr;
// 响应日志
log.info("请求完成: {} {} - {} ({}ms)",
request.getMethod(),
request.getPath(),
exchange.getResponse().getStatusCode(),
duration);
}
}));
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE; // 低优先级(最后执行)
}
}
3.6 自定义过滤器工厂
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
@Component
public class AuthGatewayFilterFactory
extends AbstractGatewayFilterFactory<AuthGatewayFilterFactory.Config> {
public AuthGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
// 获取认证信息
String token = request.getHeaders().getFirst(config.getHeaderName());
// 验证认证信息
if (token == null || !validateToken(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
};
}
private boolean validateToken(String token) {
// 验证逻辑
return true;
}
public static class Config {
private String headerName = "Authorization";
private boolean required = true;
// getter/setter
public String getHeaderName() { return headerName; }
public void setHeaderName(String headerName) { this.headerName = headerName; }
public boolean isRequired() { return required; }
public void setRequired(boolean required) { this.required = required; }
}
}
# 使用自定义过滤器
spring:
cloud:
gateway:
routes:
- id: auth-route
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- name: Auth
args:
headerName: X-Token
required: true
3.7 限流配置
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
@Configuration
public class RateLimiterConfig {
// 基于 IP 限流
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
);
}
// 基于用户限流
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getHeaders().getFirst("X-User-Id")
);
}
// 基于 API 限流
@Bean
public KeyResolver apiKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getPath().value()
);
}
// 基于参数限流
@Bean
public KeyResolver parameterKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getQueryParams().getFirst("appId")
);
}
}
spring:
cloud:
gateway:
routes:
- id: rate-limited-route
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10 # 每秒补充令牌数
redis-rate-limiter.burstCapacity: 20 # 令牌桶容量
key-resolver: "#{@ipKeyResolver}"
3.8 熔断配置
spring:
cloud:
gateway:
routes:
- id: circuit-breaker-route
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- name: CircuitBreaker
args:
name: userServiceCircuitBreaker
fallbackUri: forward:/fallback/users
statusCodes:
- 500
- 502
- 503
- 504
resilience4j:
circuitbreaker:
configs:
default:
slidingWindowSize: 10
slidingWindowType: COUNT_BASED
failureRateThreshold: 50
waitDurationInOpenState: 10s
permittedNumberOfCallsInHalfOpenState: 5
instances:
userServiceCircuitBreaker:
baseConfig: default
// 降级处理
@RestController
public class FallbackController {
@GetMapping("/fallback/users")
public Mono<Map<String, Object>> userFallback() {
Map<String, Object> result = new HashMap<>();
result.put("code", 503);
result.put("message", "用户服务暂时不可用,请稍后重试");
result.put("data", null);
return Mono.just(result);
}
@GetMapping("/fallback/orders")
public Mono<Map<String, Object>> orderFallback() {
Map<String, Object> result = new HashMap<>();
result.put("code", 503);
result.put("message", "订单服务暂时不可用,请稍后重试");
result.put("data", null);
return Mono.just(result);
}
}
3.9 跨域配置
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
- PUT
- DELETE
- OPTIONS
allowedHeaders: "*"
allowCredentials: true
maxAge: 3600
// 或使用代码配置
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOriginPattern("*");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setAllowCredentials(true);
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
四、实战应用场景
4.1 统一认证鉴权
import org.springframework.cloud.gateway.filter.*;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import reactor.core.publisher.Mono;
@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {
private final AntPathMatcher pathMatcher = new AntPathMatcher();
// 白名单路径
private final List<String> whiteList = Arrays.asList(
"/api/auth/login",
"/api/auth/register",
"/api/auth/refresh",
"/actuator/**"
);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getPath().value();
// 白名单放行
if (isWhiteListed(path)) {
return chain.filter(exchange);
}
// 获取 Token
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (token == null) {
return unauthorized(exchange, "缺少认证信息");
}
try {
// 验证 Token
Claims claims = JwtUtil.parseToken(token);
String userId = claims.getSubject();
List<String> roles = claims.get("roles", List.class);
// 检查权限
if (!checkPermission(path, exchange.getRequest().getMethod(), roles)) {
return forbidden(exchange, "无权限访问");
}
// 传递用户信息
ServerHttpRequest request = exchange.getRequest().mutate()
.header("X-User-Id", userId)
.header("X-User-Roles", String.join(",", roles))
.build();
return chain.filter(exchange.mutate().request(request).build());
} catch (Exception e) {
return unauthorized(exchange, "Token 无效或已过期");
}
}
private boolean isWhiteListed(String path) {
return whiteList.stream()
.anyMatch(pattern -> pathMatcher.match(pattern, path));
}
private boolean checkPermission(String path, HttpMethod method, List<String> roles) {
// 权限检查逻辑
return true;
}
private Mono<Void> unauthorized(ServerWebExchange exchange, String message) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
String body = String.format("{\"code\":401,\"message\":\"%s\"}", message);
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(body.getBytes());
return exchange.getResponse().writeWith(Mono.just(buffer));
}
private Mono<Void> forbidden(ServerWebExchange exchange, String message) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
String body = String.format("{\"code\":403,\"message\":\"%s\"}", message);
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(body.getBytes());
return exchange.getResponse().writeWith(Mono.just(buffer));
}
@Override
public int getOrder() {
return -100;
}
}
4.2 灰度发布
import org.springframework.cloud.gateway.filter.*;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@Component
public class GrayReleaseFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取灰度标识
String grayVersion = exchange.getRequest().getHeaders().getFirst("X-Gray-Version");
String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
// 根据用户 ID 计算是否灰度
if (isGrayUser(userId)) {
exchange.getAttributes().put("grayVersion", "v2");
} else if (grayVersion != null) {
exchange.getAttributes().put("grayVersion", grayVersion);
}
return chain.filter(exchange);
}
private boolean isGrayUser(String userId) {
// 按用户 ID 哈希取模,控制灰度比例
if (userId == null) return false;
return Math.abs(userId.hashCode()) % 100 < 10; // 10% 灰度
}
@Override
public int getOrder() {
return -50;
}
}
五、总结与最佳实践
网关设计原则
- 职责单一:网关只做路由和通用逻辑
- 无状态:网关不应保存业务状态
- 高性能:使用响应式编程
- 可扩展:支持动态路由配置
最佳实践
- 认证鉴权:在网关统一处理
- 限流熔断:保护后端服务
- 日志追踪:添加链路追踪信息
- 监控告警:监控网关性能指标
Spring Cloud Gateway 是构建微服务架构的重要组件,掌握其使用方式和最佳实践,能够构建出高性能、高可用的 API 网关系统。