Gateway 过滤器工厂与自定义过滤器源码
过滤器工厂(GatewayFilterFactory)把配置字符串转换成 GatewayFilter,是路由级过滤器的唯一来源。本文从源码拆解常用内置工厂实现,并演示自定义过滤器工厂的完整流程。
过滤器工厂机制
工厂 → 过滤器
配置:filters:
- AddRequestHeader=X-Request-Id, abc
│
▼
AddRequestHeaderGatewayFilterFactory(工厂)
│ apply(config)
▼
GatewayFilter(过滤器实例,绑定路由)java
public interface GatewayFilterFactory<C> extends ShortcutConfigurable, Configurable<C> {
String name(); // 默认取类名前缀(AddRequestHeader → addRequestHeader)
GatewayFilter apply(C config); // 工厂方法:配置 → 过滤器
}内置过滤器工厂总览
| 工厂 | 作用 |
|---|---|
| AddRequestHeader / AddRequestParameter / AddResponseHeader | 添加头/参数/响应头 |
| RemoveRequestHeader / RemoveRequestParameter / RemoveResponseHeader | 移除头/参数/响应头 |
| RewritePath / SetPath | 重写/设置请求路径 |
| PrefixPath | 添加路径前缀 |
| StripPrefix | 去除路径前缀 |
| RedirectTo | 重定向 |
| Retry | 重试 |
| RequestRateLimiter | 限流(令牌桶) |
| CircuitBreaker | 熔断降级 |
| SetRequestHostHeader | 设置 Host |
| PreserveHostHeader | 保留原始 Host |
| RequestSize | 请求体大小限制 |
| SetStatus | 设置响应状态码 |
| ModifyRequestBody / ModifyResponseBody | 修改请求/响应体 |
| SetResponseHeader / SetRequestHeader | 覆盖头 |
| CacheRequestBody | 缓存请求体 |
| SecureHeaders | 安全头 |
| SaveSession | 保存 Session |
AddRequestHeader / RemoveRequestHeader
源码
java
public class AddRequestHeaderGatewayFilterFactory
extends AbstractGatewayFilterFactory<AddRequestHeaderGatewayFilterFactory.Config> {
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
// 创建请求构建器,添加请求头
ServerHttpRequest newRequest = exchange.getRequest().mutate()
.header(config.getName(), config.getValue())
.build();
// 替换请求对象,继续过滤器链
return chain.filter(exchange.mutate().request(newRequest).build());
};
}
}配置
yaml
filters:
- AddRequestHeader=X-Request-Id, ${headerValue} # 支持 SpEL/占位符
- RemoveRequestHeader=X-Internal-Secret占位符与 SpEL
yaml
# 从请求头取值
- AddRequestHeader=X-Trace, ${x-trace}RewritePath / StripPrefix / SetPath
RewritePath:路径正则改写
yaml
- RewritePath=/api/order/(?<segment>.*), /$\{segment}源码:
java
public class RewritePathGatewayFilterFactory
extends AbstractGatewayFilterFactory<RewritePathGatewayFilterFactory.Config> {
@Override
public GatewayFilter apply(Config config) {
String regexp = config.getRegexp();
String replacement = config.getReplacement();
return (exchange, chain) -> {
ServerHttpRequest req = exchange.getRequest();
// 正则匹配原路径,替换成新路径
String newPath = req.getURI().getRawPath()
.replaceAll(regexp, replacement);
// 重建请求 URI
ServerHttpRequest newRequest = req.mutate()
.path(newPath).build();
return chain.filter(exchange.mutate().request(newRequest).build());
};
}
}注意:replacement 中的
$在 YAML 中要转义为$\{...\}。
StripPrefix:去除前 N 段
yaml
- StripPrefix=2
# /api/order/detail → /detailjava
public class StripPrefixGatewayFilterFactory
extends AbstractGatewayFilterFactory<StripPrefixGatewayFilterFactory.Config> {
@Override
public GatewayFilter apply(Config config) {
int parts = config.getParts(); // 去除前几段
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getRawPath();
String newPath = "/" + Arrays.stream(path.split("/"))
.skip(parts + 1).collect(joining("/"));
ServerHttpRequest newRequest = request.mutate().path(newPath).build();
return chain.filter(exchange.mutate().request(newRequest).build());
};
}
}SetPath:整体替换
yaml
- SetPath=/order/{orderId} # 用路径变量重组RequestRateLimiter:令牌桶限流
基于 Redis + Reactive 实现令牌桶:
yaml
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10 # 每秒补充令牌
redis-rate-limiter.burstCapacity: 20 # 桶容量
redis-rate-limiter.requestedTokens: 1 # 每次消耗令牌
key-resolver: "#{@ipKeyResolver}" # 限流键解析器源码
java
public class RequestRateLimiterGatewayFilterFactory
extends AbstractGatewayFilterFactory<RequestRateLimiterGatewayFilterFactory.Config> {
private final RateLimiter defaultRateLimiter; // RedisRateLimiter
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
// 1. 用 KeyResolver 计算限流键(IP/用户/接口)
Mono<String> routeId = resolve(exchange);
// 2. 调用限流器(Redis 原子脚本:令牌桶)
Mono<RateLimiter.Response> response = routeId
.flatMap(key -> rateLimiter.isAllowed(routeId, key));
// 3. 限流通过 → 继续;拒绝 → 429 + 错误头
return response.flatMap(r -> {
exchange.getResponse().getHeaders()
.add("X-RateLimit-Remaining", ...);
if (r.isAllowed()) return chain.filter(exchange);
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return exchange.getResponse().setComplete();
});
};
}
}自定义 KeyResolver
java
@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") != null
? exchange.getRequest().getHeaders().getFirst("X-User-Id")
: "anonymous");
}限流需要 spring-boot-starter-data-redis-reactive,令牌桶用 Lua 脚本在 Redis 中原子执行。
Retry:重试过滤器
yaml
filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY, SERVICE_UNAVAILABLE # 重试的响应状态
methods: GET
series: SERVER_ERROR
exceptions: java.io.IOException # 重试的异常
backoff: # 退避策略
firstBackoff: 100ms
maxBackoff: 1000ms
factor: 2源码要点
java
public class RetryGatewayFilterFactory
extends AbstractGatewayFilterFactory<RetryGatewayFilterFactory.Config> {
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
return chain.filter(exchange)
.retryWhen(Retry.backoff(config.getRetries(), config.getBackoff().getFirstBackoff())
.filter(retryEx -> isRetryable(config, retryEx))
.onRetryExhaustedThrow(...));
};
}
}- 只对幂等请求安全(GET),POST 重试需业务幂等
- 响应状态码可配
series(SERVER_ERROR 等)与具体statuses - 退避支持指数递增(firstBackoff × factor^n)
CircuitBreaker:熔断降级
基于 Resilience4j:
yaml
filters:
- name: CircuitBreaker
args:
name: orderServiceBreaker
fallbackUri: forward:/fallback/order # 降级转发地址
statusCodes: 500,504 # 触发熔断的状态码源码
java
public class CircuitBreakerGatewayFilterFactory
extends AbstractGatewayFilterFactory<CircuitBreakerGatewayFilterFactory.Config> {
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
// 包装为熔断保护的执行
Mono<Void> run = chain.filter(exchange)
.transformDeferred(CircuitBreakerOperator.of(breaker));
// 熔断触发 → 执行 fallback(forward 到降级接口)
if (config.getFallbackUri() != null) {
Mono<Void> fallback = forwardToFallback(exchange, config.getFallbackUri());
return run.onErrorResume(fallback);
}
return run;
};
}
}熔断器状态:CLOSED(正常)→ OPEN(熔断,直接降级)→ HALF_OPEN(试探恢复)。
自定义过滤器工厂
实现步骤
- 继承
AbstractGatewayFilterFactory<Config> - 定义 Config 内部类
- 重写
apply(config)返回 GatewayFilter - 注册为 Bean
java
@Component
public class AuthCheckGatewayFilterFactory
extends AbstractGatewayFilterFactory<AuthCheckGatewayFilterFactory.Config> {
public AuthCheckGatewayFilterFactory() {
super(Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("headerName", "requiredValue");
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
// 校验请求头是否携带指定值
String value = exchange.getRequest()
.getHeaders().getFirst(config.getHeaderName());
if (config.getRequiredValue().equals(value)) {
return chain.filter(exchange); // 通过
}
// 未通过 → 401
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
};
}
@Validated
public static class Config {
@NotEmpty private String headerName;
@NotEmpty private String requiredValue;
// getter/setter
}
}使用
yaml
filters:
- AuthCheck=X-Api-Key, secret123纯函数式写法(不用 Config)
java
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("auth-route", r -> r.path("/secure/**")
.filters(f -> f.filter((exchange, chain) -> {
// 直接写过滤器逻辑
return chain.filter(exchange);
}))
.uri("lb://secure-service"))
.build();
}过滤器执行顺序
合并后的过滤器链顺序规则:
1. 所有 GlobalFilter + 路由 GatewayFilter 合并
2. 按 Ordered 接口排序(值小优先)
3. 自定义过滤器可设置 order(@Order / Ordered)常用 order 参考:
| 过滤器 | order |
|---|---|
| RouteToRequestUrlFilter | 10000 |
| LoadBalancerClientFilter | 10100 |
| NettyRoutingFilter | 2147483647(最后转发) |
| WebSocketRoutingFilter | 2147483646 |
常见问题
- 过滤器不生效? 检查工厂名(配置前缀)与类名对应、Bean 是否注册、过滤器是否写在该路由的 filters 下。
- RewritePath 的 $ 问题? YAML 中
$\{...\}转义,否则被 YAML 解析器吞掉。 - 限流全部 429? Redis 连接异常、KeyResolver 返回 null、replenishRate/burstCapacity 配置过小。
- 熔断降级不触发? 确认 fallbackUri 对应的降级接口存在,且熔断器名(name)配置正确。