Spring Cloud Gateway 源码阅读 —— 路由定位与匹配
路由是 Gateway 的"决策表"。本文从源码拆解三件事:路由从哪来(RouteLocator)、如何缓存(RouteRefreshListener)、请求如何匹配(RoutePredicateHandlerMapping)。
RouteLocator:路由数据源
接口体系
java
// org.springframework.cloud.gateway.route.RouteLocator
public interface RouteLocator {
Flux<Route> getRoutes(); // 返回全部路由的响应式流
}RouteLocator(接口)
├── RouteDefinitionRouteLocator 核心:把 RouteDefinition 转成 Route
├── DiscoveryClientRouteDefinitionLocator 从注册中心发现服务生成路由
├── PropertiesRouteDefinitionLocator 从配置文件加载路由
└── CompositeRouteLocator 组合所有 locatorRouteDefinition 与 Route
Route 需要"谓词 + 过滤器"对象,而配置是字符串。中间的桥梁是 RouteDefinition:
java
// 配置定义(可序列化,来自 YAML/配置中心)
public class RouteDefinition {
private String id;
private URI uri;
private List<PredicateDefinition> predicates; // 字符串配置
private List<FilterDefinition> filters;
private int order;
}
// 运行时对象(可执行)
public class Route {
private String id;
private URI uri;
private int order;
private Predicate<ServerWebExchange> predicate;
private List<GatewayFilter> gatewayFilters;
}转换:RouteDefinitionRouteLocator
把定义转换成可执行 Route:
java
public class RouteDefinitionRouteLocator implements RouteLocator {
private final RouteDefinitionRepository routeDefinitionRepository; // 定义仓库
private final Map<String, RoutePredicateFactory> predicates; // 谓词工厂
private final Map<String, GatewayFilterFactory> gatewayFilterFactories; // 过滤器工厂
@Override
public Flux<Route> getRoutes() {
// 1. 从仓库取全部 RouteDefinition
return this.routeDefinitionRepository.getRouteDefinitions()
// 2. 转换:id/uri/order 直接拷贝
.map(this::convertToRoute)
// 3. 过滤掉转换失败的空路由
.filter(route -> route.getPredicate() != null);
}
private Route convertToRoute(RouteDefinition routeDefinition) {
// 用工厂把字符串配置转成对象
Predicate<ServerWebExchange> predicate = lookupPredicate(routeDefinition.getPredicates());
List<GatewayFilter> gatewayFilters = lookupGatewayFilters(routeDefinition.getFilters());
return Route.async(routeDefinition).predicate(predicate).gatewayFilters(gatewayFilters).build();
}
}谓词/过滤器查找
java
// 谓词:RouteDefinition 的谓词名 → 谓词工厂 → apply(config) → Predicate
private Predicate<ServerWebExchange> lookupPredicate(List<PredicateDefinition> definitions) {
List<Predicate<ServerWebExchange>> predicates = definitions.stream()
.map(definition -> {
RoutePredicateFactory factory = this.predicates.get(definition.getName());
Object config = factory.newConfig(); // 解析配置
// 绑定 Configurable 的快捷字段
...
return factory.apply(config); // 工厂生成谓词
})
.collect(toList());
return serverWebExchange -> predicates.stream().allMatch(p -> p.test(serverWebExchange)); // AND
}RouteDefinitionRepository:路由定义仓库
接口
java
// org.springframework.cloud.gateway.route.RouteDefinitionRepository
public interface RouteDefinitionRepository {
Flux<RouteDefinition> getRouteDefinitions(); // 读全部
Mono<Void> save(Mono<RouteDefinition> route); // 新增/更新
Mono<Void> delete(Mono<String> routeId); // 删除
}实现
| 实现 | 数据源 |
|---|---|
| InMemoryRouteDefinitionRepository | 内存(默认,支持 API 动态增删) |
| PropertiesRouteDefinitionRepository | application.yml 配置 |
| 自定义(Redis/数据库) | 实现接口即可支持动态路由管理 |
配置文件加载
java
// PropertiesRouteDefinitionRepository
public class PropertiesRouteDefinitionRepository implements RouteDefinitionRepository {
private final GatewayProperties properties; // 绑定 spring.cloud.gateway 配置
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
// 直接返回配置中的 routes 列表
return Flux.fromIterable(properties.getRoutes());
}
}路由缓存与刷新
路由加载时机
应用启动
└─ GatewayAutoConfiguration 装配
└─ RouteDefinitionRouteLocator(含仓库)
└─ 配置刷新事件 → RouteRefreshListenerRouteRefreshListener
java
// org.springframework.cloud.gateway.route.RouteRefreshListener
public class RouteRefreshListener implements ApplicationListener<ApplicationEvent> {
private final RouteDefinitionRepository routeDefinitionRepository;
private final ApplicationEventPublisher publisher;
@Override
public void onApplicationEvent(ApplicationEvent event) {
// 响应 RefreshRoutesEvent / RefreshScopeRefreshedEvent
if (event instanceof RefreshRoutesEvent || event instanceof RefreshScopeRefreshedEvent) {
// 触发路由重新加载
publishRouteDefinitionChanged();
}
}
}动态路由管理 API
POST /actuator/gateway/routes/{id} 新增/更新路由
DELETE /actuator/gateway/routes/{id} 删除路由
GET /actuator/gateway/routes 查看全部路由
POST /actuator/gateway/refresh 刷新路由缓存java
// 编程式新增路由
@Autowired
RouteDefinitionWriter routeDefinitionWriter;
RouteDefinition def = new RouteDefinition();
def.setId("dynamic-route");
def.setUri(URI.create("lb://new-service"));
def.setPredicates(Arrays.asList(new PredicateDefinition("Path=/api/new/**")));
routeDefinitionWriter.save(Mono.just(def)).subscribe();RoutePredicateHandlerMapping:请求匹配
整体匹配流程
java
// org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
private final FilteringWebHandler webHandler; // 命中后返回的处理器
private final RouteLocator routeLocator; // 路由源
@Override
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
// 1. 取全部路由
return this.routeLocator.getRoutes()
// 2. 按 order 排序(order 小的优先)
.sort(OrderedComparator.INSTANCE)
// 3. 过滤出谓词匹配的路由
.filter(route -> route.getPredicate().test(exchange))
// 4. 取第一个命中
.next()
.map(route -> {
// 5. 把路由放入 exchange 属性(后续过滤器使用)
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, route);
return this.webHandler; // 返回 FilteringWebHandler
});
}
}匹配失败
- 无路由命中 → 返回
Mono.empty()→ 404(WebFlux 默认处理) - 可通过全局过滤器统一处理 404 响应
路由属性
命中后写入 exchange 的属性(后续过滤器链可读取):
java
public static final String GATEWAY_ROUTE_ATTR = GatewayConstants.GATEWAY_ATTR + "route";
// 用法:
Route route = exchange.getAttribute(RoutePredicateHandlerMapping.GATEWAY_ROUTE_ATTR);CompositeRouteLocator:组合机制
多个 RouteLocator 并存时用组合器统一对外:
java
// org.springframework.cloud.gateway.route.CompositeRouteLocator
public class CompositeRouteLocator implements RouteLocator {
private final Flux<RouteLocator> delegates; // 组合的多个 locator
@Override
public Flux<Route> getRoutes() {
// 按 locator 的 order 排序,逐个取路由后合并
return Flux.fromIterable(delegates)
.flatMap(RouteLocator::getRoutes);
}
}java
// 自动装配(GatewayAutoConfiguration)
@Bean
public RouteLocator routeLocator(
List<RouteLocator> locators,
RouteDefinitionRouteLocator definitionRouteLocator) {
// 组合:DiscoveryClient 路由 + 定义路由 + 自定义路由
return new CompositeRouteLocator(locators);
}完整的路由来源链
请求匹配时调用的 CompositeRouteLocator
├─ 1. DiscoveryClientRouteDefinitionLocator(按服务发现自动生成路由)
├─ 2. PropertiesRouteDefinitionRepository(YAML 配置)
├─ 3. InMemoryRouteDefinitionRepository(API 动态管理)
└─ 4. 自定义 RouteLocator Bean完整时序总结
请求进入
│
▼
RoutePredicateHandlerMapping.getHandlerInternal()
│
├─ CompositeRouteLocator.getRoutes() ← 聚合所有路由源
│ ├─ RouteDefinitionRouteLocator ← 定义 → Route 转换
│ │ └─ 谓词工厂/过滤器工厂 apply
│ └─ 自定义 RouteLocator
│
├─ 排序(order)
├─ Predicate 匹配
└─ 命中 → 路由放入 exchange 属性
│
▼
FilteringWebHandler(进入过滤器链)关键源码文件索引
| 类 | 位置 |
|---|---|
| RoutePredicateHandlerMapping | gateway-server/src/main/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMapping.java |
| RouteLocator | gateway-server/.../route/RouteLocator.java |
| RouteDefinitionRouteLocator | gateway-server/.../route/RouteDefinitionRouteLocator.java |
| CompositeRouteLocator | gateway-server/.../route/CompositeRouteLocator.java |
| RouteDefinitionRepository | gateway-server/.../route/RouteDefinitionRepository.java |
| RouteRefreshListener | gateway-server/.../route/RouteRefreshListener.java |
| Route | gateway-server/.../route/Route.java |
常见问题
- 路由修改后不生效? 触发
/actuator/gateway/refresh,或发 RefreshRoutesEvent;配置中心场景自动刷新。 - 服务发现路由如何工作? DiscoveryClientRouteDefinitionLocator 按注册中心的服务名生成
lb://路由,服务上线即自动有路由。 - 多个路由来源优先级? 组合按 RouteLocator 顺序合并,最终按 Route.order 排序匹配。
- 动态路由持久化? InMemory 重启丢失,需自定义 RouteDefinitionRepository(Redis/MySQL)。