Spring Cloud Gateway、Zuul 统一入口与路由
在微服务架构中,API 网关作为系统的统一入口,提供以下核心功能:
Route(路由):网关的基本构建块,包含 ID、目标 URI、断言集合和过滤器集合。
Predicate(断言):匹配 HTTP 请求的条件,如路径、方法、请求头等。
Filter(过滤器):在请求前后执行的逻辑,如修改请求头、限流、日志记录等。
// pom.xml 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
// 启动类
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
# application.yml 基础配置
server:
port: 8080
spring:
application:
name: api-gateway
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
# 多种断言方式
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
# 路径匹配
- Path=/api/users/**
# 方法匹配
- Method=GET,POST
# 请求头匹配
- Header=X-Request-Id, \d+
# 查询参数匹配
- Query=token
# Cookie 匹配
- Cookie=session, abc.*
# Host 匹配
- Host=**.example.com
# 时间匹配(在指定时间后生效)
- After=2024-01-01T00:00:00+08:00[Asia/Shanghai]
# 权重路由(灰度发布)
- Weight=group1, 8
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
# 去除路径前缀
- StripPrefix=1
# 添加请求头
- AddRequestHeader=X-Request-Source, gateway
# 添加响应头
- AddResponseHeader=X-Response-Time, ${timestamp}
# 添加请求参数
- AddRequestParameter=source, gateway
# 重定向
- RedirectTo=302, https://example.com
# 重写路径
- RewritePath=/api/(?<segment>.*), /$\{segment}
# 限流
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
# 熔断
- name: CircuitBreaker
args:
name: userServiceCircuitBreaker
fallbackUri: forward:/fallback/users
# 重试
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY,GATEWAY_TIMEOUT
methods: GET,POST
backoff:
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
@Component
@Order(-1) // 优先级,数字越小优先级越高
public class AuthGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 获取 Token
String token = request.getHeaders().getFirst("Authorization");
// 白名单路径
String path = request.getPath().value();
if (path.startsWith("/api/public/")) {
return chain.filter(exchange);
}
// 验证 Token
if (token == null || !validateToken(token)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
// 添加用户信息到请求头
String userId = extractUserId(token);
ServerHttpRequest mutatedRequest = request.mutate()
.header("X-User-Id", userId)
.build();
ServerWebExchange mutatedExchange = exchange.mutate()
.request(mutatedRequest)
.build();
return chain.filter(mutatedExchange);
}
private boolean validateToken(String token) {
// Token 验证逻辑
return true;
}
private String extractUserId(String token) {
// 提取用户 ID
return "123";
}
}
@Component
public class LoggingGatewayFilterFactory
extends AbstractGatewayFilterFactory<LoggingGatewayFilterFactory.Config> {
public LoggingGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
// 请求前日志
if (config.isPreLogger()) {
System.out.println("请求路径: " + request.getPath());
System.out.println("请求方法: " + request.getMethod());
}
// 记录开始时间
exchange.getAttributes().put("startTime", System.currentTimeMillis());
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// 响应后日志
if (config.isPostLogger()) {
Long startTime = exchange.getAttribute("startTime");
Long duration = System.currentTimeMillis() - startTime;
System.out.println("响应状态: " + exchange.getResponse().getStatusCode());
System.out.println("响应时间: " + duration + "ms");
}
}));
};
}
public static class Config {
private boolean preLogger = true;
private boolean postLogger = true;
// Getters and Setters
public boolean isPreLogger() { return preLogger; }
public void setPreLogger(boolean preLogger) { this.preLogger = preLogger; }
public boolean isPostLogger() { return postLogger; }
public void setPostLogger(boolean postLogger) { this.postLogger = postLogger; }
}
}
# 使用自定义过滤器
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- name: Logging
args:
preLogger: true
postLogger: true
// pom.xml 添加 Redis 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
# application.yml 配置
spring:
redis:
host: localhost
port: 6379
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- name: RequestRateLimiter
args:
# 令牌桶每秒填充速率
redis-rate-limiter.replenishRate: 10
# 令牌桶容量
redis-rate-limiter.burstCapacity: 20
# 限流 Key 解析器
key-resolver: "#{@ipKeyResolver}"
// 自定义限流 Key 解析器
@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")
);
}
@Bean
public KeyResolver apiKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getPath().value()
);
}
// pom.xml 添加 Resilience4j 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
# application.yml 配置
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- name: CircuitBreaker
args:
name: userServiceCircuitBreaker
fallbackUri: forward:/fallback/users
resilience4j:
circuitbreaker:
instances:
userServiceCircuitBreaker:
# 滑动窗口大小
slidingWindowSize: 10
# 失败率阈值
failureRateThreshold: 50
# 等待时间
waitDurationInOpenState: 10000
# 半开状态允许的调用数
permittedNumberOfCallsInHalfOpenState: 3
// 降级处理器
@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", "用户服务暂时不可用,请稍后重试");
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", "订单服务暂时不可用,请稍后重试");
return Mono.just(result);
}
}
# application.yml 配置
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 corsWebFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
@Service
public class DynamicRouteService {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
@Autowired
private ApplicationEventPublisher publisher;
// 添加路由
public String addRoute(RouteDefinition definition) {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
}
// 删除路由
public String deleteRoute(String id) {
routeDefinitionWriter.delete(Mono.just(id)).subscribe();
publisher.publishEvent(new RefreshRoutesEvent(this));
return "success";
}
// 更新路由
public String updateRoute(RouteDefinition definition) {
deleteRoute(definition.getId());
addRoute(definition);
return "success";
}
}
@RestController
@RequestMapping("/route")
public class RouteController {
@Autowired
private DynamicRouteService dynamicRouteService;
@PostMapping("/add")
public String addRoute(@RequestBody RouteDefinition definition) {
return dynamicRouteService.addRoute(definition);
}
@DeleteMapping("/delete/{id}")
public String deleteRoute(@PathVariable String id) {
return dynamicRouteService.deleteRoute(id);
}
@PutMapping("/update")
public String updateRoute(@RequestBody RouteDefinition definition) {
return dynamicRouteService.updateRoute(definition);
}
}
// pom.xml 添加 Actuator 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# application.yml 配置
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
gateway:
enabled: true
# 访问网关端点
GET /actuator/gateway/routes # 查看所有路由
GET /actuator/gateway/routes/{id} # 查看指定路由
POST /actuator/gateway/refresh # 刷新路由
GET /actuator/gateway/globalfilters # 查看全局过滤器
GET /actuator/gateway/routefilters # 查看路由过滤器
Q: Gateway 和 Zuul 如何选择?
A: Gateway 基于 WebFlux 响应式编程,性能更好,是官方推荐方案。Zuul 1.x 基于 Servlet,已停止维护。
Q: 网关如何处理超时?
A: 配置 connect-timeout 和 response-timeout,结合熔断降级保护系统。建议超时时间设置为 5-10 秒。
API 网关是微服务的统一入口。通过本课学习,你应该掌握: