Spring Cloud Gateway
Spring Cloud Gateway是基于Spring Boot 2.x、Spring WebFlux和Project Reactor构建的响应式API网关,为微服务架构提供简单、有效且统一的API路由方式。
核心特性
响应式架构
- 非阻塞I/O - 基于Netty和Spring WebFlux实现
- 背压支持 - 天然支持流量控制和背压处理
- 高并发 - 支持大量并发连接,资源利用率高
- 内存效率 - 相比传统阻塞式网关,内存使用更高效
路由功能
- 动态路由 - 支持运行时动态修改路由配置
- 路径重写 - 灵活的URL路径转换和重写
- 负载均衡 - 集成Spring Cloud LoadBalancer
- 服务发现 - 与Eureka、Consul、Nacos等注册中心集成
过滤器机制
- 全局过滤器 - 应用于所有路由的过滤逻辑
- 路由过滤器 - 特定路由的过滤处理
- 自定义过滤器 - 灵活扩展业务逻辑
- 过滤器链 - 支持多个过滤器的链式处理
核心概念
基本组件
- Route - 路由,网关的基本构建块
- Predicate - 谓词,用于匹配HTTP请求
- Filter - 过滤器,处理请求和响应
- Gateway Handler Mapping - 路由映射处理器
执行流程
请求执行流程:
1. 客户端请求到达Gateway
2. Gateway Handler Mapping确定路由
3. 发送到Gateway Web Handler
4. 通过过滤器链处理请求
5. 代理请求到下游服务
6. 通过过滤器链处理响应
7. 返回响应给客户端快速开始
Maven依赖
点击查看完整代码实现
xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 服务发现 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 限流 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
</dependencies>基础配置
application.yml配置
点击查看完整代码实现
yaml
server:
port: 8080
spring:
application:
name: api-gateway
cloud:
gateway:
# 全局CORS配置
globalcors:
cors-configurations:
'[/**]':
allowed-origins: "*"
allowed-methods: "*"
allowed-headers: "*"
allow-credentials: true
# 路由配置
routes:
# 用户服务路由
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- StripPrefix=2
- AddRequestHeader=X-Request-Source, gateway
- AddResponseHeader=X-Response-Source, gateway
# 订单服务路由
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
- Method=GET,POST
filters:
- StripPrefix=2
- RewritePath=/api/orders/(?<segment>.*), /orders/$\{segment}
# 商品服务路由
- id: product-service
uri: lb://product-service
predicates:
- Path=/api/products/**
- Header=X-Request-Id, \d+
filters:
- StripPrefix=2
- RequestRateLimiter=#{@redisRateLimiter}
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@userKeyResolver}"
# 服务发现配置
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
# Redis配置(用于限流)
redis:
host: localhost
port: 6379
database: 0基础示例
网关主类
点击查看完整代码实现
java
@SpringBootApplication
@EnableEurekaClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
// 用户限流Key解析器
@Bean
@Primary
public KeyResolver userKeyResolver() {
return exchange -> Mono.just("user");
}
// IP限流Key解析器
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
);
}
// 路径限流Key解析器
@Bean
public KeyResolver pathKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
}Java配置方式
点击查看完整代码实现
java
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// 用户服务路由
.route("user-service", r -> r
.path("/api/users/**")
.and()
.method(HttpMethod.GET, HttpMethod.POST)
.filters(f -> f
.stripPrefix(2)
.addRequestHeader("X-Request-Source", "gateway")
.addResponseHeader("X-Response-Source", "gateway")
.retry(config -> config
.retries(3)
.series(HttpStatus.Series.SERVER_ERROR)
.backoff(Duration.ofSeconds(1), Duration.ofSeconds(10), 2, false)
)
)
.uri("lb://user-service")
)
// 重定向路由
.route("redirect-route", r -> r
.path("/old-api/**")
.filters(f -> f.redirect(302, "/new-api"))
.uri("no://op")
)
// 限流路由
.route("throttle-route", r -> r
.path("/api/throttle/**")
.filters(f -> f
.requestRateLimiter(config -> config
.setRateLimiter(redisRateLimiter())
.setKeyResolver(exchange -> Mono.just("throttle"))
)
)
.uri("lb://throttle-service")
)
.build();
}
@Bean
public RedisRateLimiter redisRateLimiter() {
return new RedisRateLimiter(10, 20, 1);
}
}路由谓词
内置谓词
点击查看完整代码实现
yaml
spring:
cloud:
gateway:
routes:
# 时间相关谓词
- id: after-route
uri: lb://service
predicates:
- After=2024-01-01T00:00:00.000+08:00[Asia/Shanghai]
- id: before-route
uri: lb://service
predicates:
- Before=2024-12-31T23:59:59.999+08:00[Asia/Shanghai]
- id: between-route
uri: lb://service
predicates:
- Between=2024-01-01T00:00:00.000+08:00[Asia/Shanghai], 2024-12-31T23:59:59.999+08:00[Asia/Shanghai]
# 请求相关谓词
- id: cookie-route
uri: lb://service
predicates:
- Cookie=sessionId, \w+
- id: header-route
uri: lb://service
predicates:
- Header=X-Request-Id, \d+
- id: host-route
uri: lb://service
predicates:
- Host=api.example.com,gateway.example.com
- id: method-route
uri: lb://service
predicates:
- Method=GET,POST
- id: path-route
uri: lb://service
predicates:
- Path=/api/v1/**,/api/v2/**
- id: query-route
uri: lb://service
predicates:
- Query=version, v\d+
- id: remote-addr-route
uri: lb://service
predicates:
- RemoteAddr=192.168.1.0/24
# 权重路由
- id: weight-high
uri: lb://service-v2
predicates:
- Weight=group1, 8
- id: weight-low
uri: lb://service-v1
predicates:
- Weight=group1, 2自定义谓词
点击查看完整代码实现
java
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {
public MyRoutePredicateFactory() {
super(Config.class);
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
// 自定义匹配逻辑
String userAgent = exchange.getRequest().getHeaders().getFirst("User-Agent");
return userAgent != null && userAgent.contains(config.getUserAgent());
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("userAgent");
}
public static class Config {
private String userAgent;
// getter and setter
public String getUserAgent() {
return userAgent;
}
public void setUserAgent(String userAgent) {
this.userAgent = userAgent;
}
}
}过滤器开发
全局过滤器
点击查看完整代码实现
java
@Component
@Order(1)
public class AuthGlobalFilter implements GlobalFilter {
private static final String AUTH_TOKEN = "Authorization";
private static final List<String> EXCLUDE_PATHS = Arrays.asList(
"/api/auth/login", "/api/auth/register", "/actuator/health"
);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().pathWithinApplication().value();
// 排除不需要认证的路径
if (EXCLUDE_PATHS.stream().anyMatch(path::startsWith)) {
return chain.filter(exchange);
}
// 检查认证token
String token = request.getHeaders().getFirst(AUTH_TOKEN);
if (StringUtils.isEmpty(token)) {
return unauthorized(exchange.getResponse());
}
// 验证token
return validateToken(token)
.flatMap(valid -> {
if (valid) {
// 添加用户信息到请求头
ServerHttpRequest newRequest = request.mutate()
.header("X-User-Id", getUserId(token))
.header("X-User-Role", getUserRole(token))
.build();
return chain.filter(exchange.mutate().request(newRequest).build());
} else {
return unauthorized(exchange.getResponse());
}
});
}
private Mono<Void> unauthorized(ServerHttpResponse response) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().add("Content-Type", "application/json");
String body = "{\"code\":401,\"message\":\"Unauthorized\"}";
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes());
return response.writeWith(Mono.just(buffer));
}
private Mono<Boolean> validateToken(String token) {
// 异步验证token
return Mono.fromCallable(() -> {
// 模拟token验证逻辑
return token.startsWith("Bearer ") && token.length() > 20;
})
.subscribeOn(Schedulers.boundedElastic());
}
private String getUserId(String token) {
// 从token中提取用户ID
return "user123";
}
private String getUserRole(String token) {
// 从token中提取用户角色
return "USER";
}
}日志过滤器
点击查看完整代码实现
java
@Component
@Order(2)
public class LoggingGlobalFilter implements GlobalFilter {
private static final Logger log = LoggerFactory.getLogger(LoggingGlobalFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 记录请求信息
String requestId = UUID.randomUUID().toString();
log.info("Gateway Request: {} {} {} from {}",
requestId,
request.getMethod(),
request.getPath().pathWithinApplication().value(),
request.getRemoteAddress()
);
// 记录开始时间
exchange.getAttributes().put("startTime", System.currentTimeMillis());
exchange.getAttributes().put("requestId", requestId);
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
Long startTime = exchange.getAttribute("startTime");
if (startTime != null) {
long duration = System.currentTimeMillis() - startTime;
ServerHttpResponse response = exchange.getResponse();
log.info("Gateway Response: {} {} {} - {} in {}ms",
requestId,
request.getMethod(),
request.getPath().pathWithinApplication().value(),
response.getStatusCode(),
duration
);
}
})
);
}
}自定义路由过滤器
点击查看完整代码实现
java
@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.Config> {
public CustomGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
// 添加自定义请求头
ServerHttpRequest newRequest = request.mutate()
.header("X-Custom-Header", config.getValue())
.build();
return chain.filter(exchange.mutate().request(newRequest).build())
.then(Mono.fromRunnable(() -> {
// 处理响应
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().add("X-Custom-Response", config.getValue());
}));
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("value");
}
public static class Config {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}高级功能
熔断降级
点击查看完整代码实现
yaml
spring:
cloud:
gateway:
routes:
- id: hystrix-route
uri: lb://service
predicates:
- Path=/api/hystrix/**
filters:
- StripPrefix=2
- name: CircuitBreaker
args:
name: myCircuitBreaker
fallbackUri: forward:/fallback
# Resilience4j配置
resilience4j:
circuitbreaker:
instances:
myCircuitBreaker:
sliding-window-size: 10
minimum-number-of-calls: 5
failure-rate-threshold: 50
wait-duration-in-open-state: 30000
permitted-number-of-calls-in-half-open-state: 3java
@RestController
public class FallbackController {
@RequestMapping("/fallback")
public Mono<Map<String, Object>> fallback(ServerWebExchange exchange) {
Map<String, Object> result = new HashMap<>();
result.put("code", 503);
result.put("message", "Service temporarily unavailable");
result.put("timestamp", Instant.now().toString());
return Mono.just(result);
}
}重试机制
点击查看完整代码实现
yaml
spring:
cloud:
gateway:
routes:
- id: retry-route
uri: lb://service
predicates:
- Path=/api/retry/**
filters:
- StripPrefix=2
- name: Retry
args:
retries: 3
series: SERVER_ERROR
methods: GET,POST
backoff:
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
basedOnPreviousValue: false请求去重
点击查看完整代码实现
java
@Component
public class DedupeGlobalFilter implements GlobalFilter, Ordered {
private final RedisTemplate<String, Object> redisTemplate;
public DedupeGlobalFilter(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 生成请求唯一标识
String dedupeKey = generateDedupeKey(request);
return Mono.fromCallable(() -> {
// 检查是否重复请求
Boolean exists = redisTemplate.hasKey("dedupe:" + dedupeKey);
if (Boolean.TRUE.equals(exists)) {
return false; // 重复请求
}
// 设置去重标记,5分钟过期
redisTemplate.opsForValue().set("dedupe:" + dedupeKey, true, Duration.ofMinutes(5));
return true; // 非重复请求
})
.subscribeOn(Schedulers.boundedElastic())
.flatMap(isUnique -> {
if (isUnique) {
return chain.filter(exchange);
} else {
// 返回重复请求错误
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.CONFLICT);
response.getHeaders().add("Content-Type", "application/json");
String body = "{\"code\":409,\"message\":\"Duplicate request\"}";
DataBuffer buffer = response.bufferFactory().wrap(body.getBytes());
return response.writeWith(Mono.just(buffer));
}
});
}
private String generateDedupeKey(ServerHttpRequest request) {
// 基于用户ID、请求路径、请求参数生成唯一标识
String userId = request.getHeaders().getFirst("X-User-Id");
String path = request.getPath().value();
String queryParams = request.getQueryParams().toString();
return DigestUtils.md5DigestAsHex((userId + path + queryParams).getBytes());
}
@Override
public int getOrder() {
return 100;
}
}监控与运维
Actuator监控
yaml
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
gateway:
enabled: true
info:
java:
enabled: truejava
// 获取路由信息
GET /actuator/gateway/routes
// 获取全局过滤器
GET /actuator/gateway/globalfilters
// 获取路由过滤器
GET /actuator/gateway/routefilters
// 刷新路由
POST /actuator/gateway/refresh
// 获取特定路由详情
GET /actuator/gateway/routes/{id}Prometheus指标
yaml
management:
metrics:
export:
prometheus:
enabled: true点击查看完整代码实现
java
@Component
public class CustomMetrics {
private final Counter requestCounter;
private final Timer requestTimer;
private final Gauge activeConnections;
public CustomMetrics(MeterRegistry meterRegistry) {
this.requestCounter = Counter.builder("gateway.requests.total")
.description("Total number of gateway requests")
.register(meterRegistry);
this.requestTimer = Timer.builder("gateway.requests.duration")
.description("Gateway request duration")
.register(meterRegistry);
this.activeConnections = Gauge.builder("gateway.connections.active")
.description("Active connections")
.register(meterRegistry, this, CustomMetrics::getActiveConnections);
}
private double getActiveConnections() {
// 返回活跃连接数
return 100.0;
}
}动态路由管理
点击查看完整代码实现
java
@RestController
@RequestMapping("/admin/routes")
public class RouteController {
@Autowired
private InMemoryRouteDefinitionRepository routeDefinitionRepository;
@Autowired
private ApplicationEventPublisher publisher;
@PostMapping
public Mono<ResponseEntity<Object>> add(@RequestBody RouteDefinition definition) {
return routeDefinitionRepository.save(Mono.just(definition))
.then(Mono.fromRunnable(() -> publisher.publishEvent(new RefreshRoutesEvent(this))))
.then(Mono.just(ResponseEntity.ok().build()));
}
@PutMapping("/{id}")
public Mono<ResponseEntity<Object>> update(@PathVariable String id, @RequestBody RouteDefinition definition) {
return routeDefinitionRepository.save(Mono.just(definition.toBuilder().id(id).build()))
.then(Mono.fromRunnable(() -> publisher.publishEvent(new RefreshRoutesEvent(this))))
.then(Mono.just(ResponseEntity.ok().build()));
}
@DeleteMapping("/{id}")
public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
return routeDefinitionRepository.delete(Mono.just(id))
.then(Mono.fromRunnable(() -> publisher.publishEvent(new RefreshRoutesEvent(this))))
.then(Mono.just(ResponseEntity.ok().build()));
}
@GetMapping
public Flux<RouteDefinition> list() {
return routeDefinitionRepository.getRouteDefinitions();
}
}最佳实践
性能优化
- 合理设置Netty连接池和线程池参数
- 启用HTTP/2支持提升传输效率
- 使用缓存减少重复计算和外部调用
- 监控内存使用避免内存泄漏
安全配置
- 实现统一的认证授权机制
- 配置CORS策略控制跨域访问
- 实现请求限流防止DDoS攻击
- 过滤恶意请求和敏感信息
可靠性保证
- 配置熔断降级机制
- 实现优雅的错误处理
- 建立健康检查和监控告警
- 制定故障应急处理流程
运维管理
- 实现动态路由配置管理
- 建立全链路日志追踪
- 配置性能监控和统计分析
- 定期进行压力测试和性能调优
Spring Cloud Gateway作为新一代的响应式API网关,凭借其出色的性能表现、丰富的功能特性和与Spring生态的完美集成,成为微服务架构中API网关的首选方案。
