SpringCloud网关:Gateway路由配置与过滤器链
文章目录
- 引言
- 一、Gateway基本架构
- 二、路由配置方式
- 2.1 配置文件方式
- 2.2 Java代码方式
- 三、内置断言工厂
- 四、内置过滤器工厂
- 4.1 请求路径相关过滤器
- 4.2 请求和响应头过滤器
- 4.3 功能性过滤器
- 五、自定义过滤器
- 5.1 自定义GatewayFilter
- 5.2 自定义过滤器工厂
- 六、全局过滤器
- 总结
引言
在微服务架构中,API网关是整个系统的入口,负责请求路由、负载均衡、认证鉴权、限流熔断等关键功能。Spring Cloud Gateway作为Spring Cloud生态中的新一代API网关,基于Spring WebFlux和Reactor构建,提供了非阻塞、响应式的API网关解决方案。相比于Zuul,Gateway具有更强的性能和更丰富的功能。本文将深入探讨Spring Cloud Gateway的核心概念、路由配置方法以及过滤器链机制,帮助开发者构建高效的微服务网关。
一、Gateway基本架构
Spring Cloud Gateway的核心架构包括路由(Route)、断言(Predicate)和过滤器(Filter)三大组件。路由是网关的基本单元,包含目标URI、断言集合和过滤器集合。断言用于判断请求是否满足某种条件,满足条件的请求会被路由到指定服务。过滤器则用于对请求和响应进行修改,实现各种横切功能。
/**
* Gateway的基本工作流程:
* 1. 客户端发送请求到Gateway
* 2. Gateway的HandlerMapping组件找到与请求匹配的路由
* 3. 请求经过一系列过滤器的处理
* 4. 到达目标服务并获取响应
* 5. 响应再次经过过滤器的处理
* 6. 最终返回给客户端
*/
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
Gateway的核心依赖:
<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>
二、路由配置方式
Spring Cloud Gateway提供了两种路由配置方式:配置文件方式和Java代码方式。开发者可以根据实际需求选择适合的配置方式。
2.1 配置文件方式
通过application.yml文件可以方便地配置路由规则,这种方式简单直观,适合大多数场景:
spring:
cloud:
gateway:
routes:
- id: user-service-route # 路由ID,需保持唯一
uri: lb://user-service # 目标URI,lb表示使用负载均衡
predicates: # 断言条件,满足条件的请求会被路由
- Path=/api/users/** # 路径匹配断言
- Method=GET,POST # HTTP方法匹配断言
- Header=X-Request-Id, \d+ # 请求头匹配断言
filters: # 过滤器
- StripPrefix=1 # 去除路径前缀
- AddRequestHeader=X-Gateway-Timestamp, ${now} # 添加请求头
- id: order-service-route
uri: lb://order-service
predicates:
- Path=/api/orders/**
- Between=2022-01-01T00:00:00+08:00[Asia/Shanghai],2023-12-31T23:59:59+08:00[Asia/Shanghai] # 时间范围断言
filters:
- StripPrefix=1
- name: RequestRateLimiter # 限流过滤器
args:
redis-rate-limiter.replenishRate: 10 # 令牌桶填充速率
redis-rate-limiter.burstCapacity: 20 # 令牌桶容量
key-resolver: "#{@userKeyResolver}" # 限流键解析器
2.2 Java代码方式
使用Java代码配置路由提供了更灵活的编程能力,可以根据条件动态生成路由规则:
/**
* 使用Java代码配置路由
*/
@Configuration
public class RouteConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
// 用户服务路由
.route("user-service-route", r -> r
.path("/api/users/**")
.and().method("GET", "POST")
.filters(f -> f
.stripPrefix(1)
.addRequestHeader("X-Gateway-Timestamp",
String.valueOf(System.currentTimeMillis()))
.requestRateLimiter(c -> c
.setRateLimiter(redisRateLimiter())
.setKeyResolver(userKeyResolver()))
)
.uri("lb://user-service"))
// 订单服务路由
.route("order-service-route", r -> r
.path("/api/orders/**")
.filters(f -> f
.stripPrefix(1)
.retry(retryConfig -> retryConfig
.setRetries(3)
.setStatuses(HttpStatus.INTERNAL_SERVER_ERROR))
)
.uri("lb://order-service"))
// 重定向路由
.route("redirect-route", r -> r
.path("/redirect/**")
.filters(f -> f.redirect(302, "https://www.example.com"))
.uri("no://op"))
.build();
}
/**
* Redis限流器
*/
@Bean
public RedisRateLimiter redisRateLimiter() {
return new RedisRateLimiter(10, 20);
}
/**
* 限流键解析器,基于用户ID
*/
@Bean
public KeyResolver userKeyResolver() {
return exchange -> {
String userId = exchange.getRequest().getHeaders().getFirst("X-User-Id");
if (userId == null) {
userId = "anonymous";
}
return Mono.just(userId);
};
}
}
三、内置断言工厂
Spring Cloud Gateway提供了多种内置断言工厂,用于根据不同条件匹配路由。了解这些断言工厂的用法可以帮助开发者实现精细的路由控制。
常用的断言工厂包括:
- Path断言工厂:根据请求路径匹配路由
- Method断言工厂:根据HTTP方法匹配路由
- Header断言工厂:根据请求头匹配路由
- Query断言工厂:根据查询参数匹配路由
- Cookie断言工厂:根据Cookie匹配路由
- Host断言工厂:根据主机名匹配路由
- Between断言工厂:根据时间范围匹配路由
- Weight断言工厂:按比例将请求路由到不同服务
# 断言工厂使用示例
spring:
cloud:
gateway:
routes:
- id: complex-route
uri: lb://example-service
predicates:
- Path=/api/{segment}/{id} # 路径变量
- Method=GET
- Query=version, v[1-3] # 查询参数正则匹配
- Header=X-API-Version, \d+ # 请求头正则匹配
- Cookie=session, \w+ # Cookie正则匹配
- Host=**.example.org # 主机名通配符匹配
- Weight=group1, 8 # 按8:2的比例路由
四、内置过滤器工厂
Gateway的过滤器是实现网关功能的核心组件,可以对请求和响应进行各种处理。Gateway提供了丰富的内置过滤器工厂,满足常见的网关功能需求。
4.1 请求路径相关过滤器
filters:
# 去除前缀
- StripPrefix=1
# 添加前缀
- PrefixPath=/api
# 路径重写
- RewritePath=/api/(?<segment>.*), /$\{segment}
# 设置路径变量
- SetPath=/v2/{segment}
4.2 请求和响应头过滤器
filters:
# 添加请求头
- AddRequestHeader=X-Request-Id, ${requestId}
# 添加响应头
- AddResponseHeader=X-Response-Time, ${responseTime}
# 移除请求头
- RemoveRequestHeader=X-Unwanted-Header
# 移除响应头
- RemoveResponseHeader=X-Internal-Header
# 修改请求头
- MapRequestHeader=X-Old-Header, X-New-Header
4.3 功能性过滤器
filters:
# 重定向
- RedirectTo=302, https://example.org
# 请求大小限制
- RequestSize=5MB
# 重试
- name: Retry
args:
retries: 3
statuses: INTERNAL_SERVER_ERROR
methods: GET,POST
# 熔断
- name: CircuitBreaker
args:
name: defaultCircuitBreaker
fallbackUri: forward:/fallback
# 限流
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@userKeyResolver}"
五、自定义过滤器
除了使用内置过滤器外,Gateway还支持自定义过滤器,以满足特定业务需求。自定义过滤器有两种方式:实现GatewayFilter接口或继承AbstractGatewayFilterFactory类。
5.1 自定义GatewayFilter
/**
* 自定义GatewayFilter
* 记录请求处理时间
*/
@Component
public class RequestTimeFilter implements GatewayFilter, Ordered {
private static final Logger log = LoggerFactory.getLogger(RequestTimeFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
exchange.getAttributes().put("requestTimeBegin", System.currentTimeMillis());
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
Long startTime = exchange.getAttribute("requestTimeBegin");
if (startTime != null) {
Long endTime = System.currentTimeMillis();
log.info("Request {} processed in {} ms",
exchange.getRequest().getURI().getPath(),
endTime - startTime);
}
})
);
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
/**
* 在路由配置中使用自定义过滤器
*/
@Configuration
public class CustomFilterConfig {
@Bean
public RouteLocator customFilterRoute(RouteLocatorBuilder builder, RequestTimeFilter timeFilter) {
return builder.routes()
.route("custom-filter-route", r -> r
.path("/api/custom/**")
.filters(f -> f.filter(timeFilter))
.uri("lb://custom-service"))
.build();
}
}
5.2 自定义过滤器工厂
/**
* 自定义过滤器工厂
* 添加请求日志
*/
@Component
public class RequestLogGatewayFilterFactory extends AbstractGatewayFilterFactory<RequestLogGatewayFilterFactory.Config> {
private static final Logger log = LoggerFactory.getLogger(RequestLogGatewayFilterFactory.class);
public RequestLogGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
// 记录请求信息
if (config.isLogHeaders()) {
log.info("Request headers: {}", request.getHeaders());
}
log.info("Request method: {}, path: {}, client: {}",
request.getMethod(),
request.getURI().getPath(),
request.getRemoteAddress());
return chain.filter(exchange);
};
}
/**
* 过滤器配置类
*/
public static class Config {
private boolean logHeaders;
public boolean isLogHeaders() {
return logHeaders;
}
public void setLogHeaders(boolean logHeaders) {
this.logHeaders = logHeaders;
}
}
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList("logHeaders");
}
}
在配置文件中使用自定义过滤器工厂:
spring:
cloud:
gateway:
routes:
- id: custom-filter-factory-route
uri: lb://example-service
predicates:
- Path=/api/example/**
filters:
- RequestLog=true # 使用自定义过滤器工厂,参数为logHeaders
六、全局过滤器
全局过滤器会应用到所有路由上,适合实现通用功能,如认证、监控、日志等。实现GlobalFilter接口可以创建全局过滤器。
/**
* 全局认证过滤器
* 对所有请求进行JWT认证
*/
@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {
@Autowired
private JwtTokenValidator tokenValidator;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 跳过公开API
if (isPublicApi(request.getURI().getPath())) {
return chain.filter(exchange);
}
// 获取token
String token = getTokenFromRequest(request);
if (token == null) {
return unauthorized(exchange);
}
// 验证token
if (!tokenValidator.validate(token)) {
return unauthorized(exchange);
}
// 解析用户信息并放入请求头
String userId = tokenValidator.getUserId(token);
ServerHttpRequest mutatedRequest = request.mutate()
.header("X-User-Id", userId)
.build();
return chain.filter(exchange.mutate().request(mutatedRequest).build());
}
private boolean isPublicApi(String path) {
return path.startsWith("/api/public/") || path.startsWith("/auth/");
}
private String getTokenFromRequest(ServerHttpRequest request) {
List<String> authHeaders = request.getHeaders().get("Authorization");
if (authHeaders != null && !authHeaders.isEmpty()) {
String authHeader = authHeaders.get(0);
if (authHeader.startsWith("Bearer ")) {
return authHeader.substring(7);
}
}
return null;
}
private Mono<Void> unauthorized(ServerWebExchange exchange) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
@Override
public int getOrder() {
return -100; // 确保认证过滤器先执行
}
}
总结
Spring Cloud Gateway作为新一代API网关,凭借其基于WebFlux的响应式特性,提供了高性能的请求路由和处理能力。本文介绍了Gateway的基本架构、路由配置方法、断言工厂使用、过滤器链机制以及自定义过滤器的实现方式。通过合理配置路由和过滤器,可以实现请求转发、负载均衡、认证鉴权、限流熔断等微服务网关的核心功能。
在实际应用中,开发者可以根据具体需求选择配置文件或Java代码方式定义路由,组合使用内置断言和过滤器,或实现自定义过滤器和全局过滤器,构建功能完备的API网关。随着微服务架构的不断发展,Gateway的非阻塞特性和丰富的功能使其成为构建现代微服务网关的理想选择。