当前章节主要是讲解配置文件中定义的路由配置被gateway加载,同时转为可以直接操作的路由对象
引入pom坐标
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
前提:需要对springboot的自动装配要非常的熟悉,还有对reactor编程有一些了解,在分析过程中会对reactor一些基础进行讲一下
1. 找到组件加载入口spring.factories
在包spring-cloud-gateway-server包下,找到spring.factories文件(springboot的自动装配)
2. GatewayAutoConfiguration 自动装配类
最主要的装配类为GatewayAutoConfiguration
3. 加载配置文件中路由信息
@Bean
public GatewayProperties gatewayProperties() {
return new GatewayProperties();
}
4. GatewayProperties对象
我们在配置文件中配置的路由规则会在启动的时候都加载到该对象中
@ConfigurationProperties(GatewayProperties.PREFIX)
@Validated
public class GatewayProperties {
public static final String PREFIX = "spring.cloud.gateway";
//路由配置
private List<RouteDefinition> routes = new ArrayList<>();
//默认的过滤器
private List<FilterDefinition> defaultFilters = new ArrayList<>();
private List<MediaType> streamingMediaTypes = Arrays
.asList(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_STREAM_JSON);
private boolean failOnRouteDefinitionError = true;
// 统计配置
private Metrics metrics = new Metrics();
}
5. RouteDefinition每个路由的详细配置
路由的格式
spring:
cloud:
gateway:
routes:
- id: test
uri: http://localhost:8081
predicates:
- Path=/test/**
- id: test2
uri: http://localhost:8082
predicates:
- Path=/api/**
对应的路由配置
public class RouteDefinition {
// 路由唯一标识
private String id;
// 路由规则
private List<PredicateDefinition> predicates = new ArrayList<>();
// 指定路由的过滤器
private List<FilterDefinition> filters = new ArrayList<>();
// 路由匹配后请求的地址
private URI uri;
// 元数据
private Map<String, Object> metadata = new HashMap<>();
// 加载序列
private int order = 0;
}
6. 将路由的定义信息转为路由控制对象
在装配类GatewayAutoConfiguration中找到一下装配对象
@Bean
public RouteLocator routeDefinitionRouteLocator(
GatewayProperties properties, // 路由定义信息
List<GatewayFilterFactory> gatewayFilters, // 过滤器,之后的章节中进行分析
List<RoutePredicateFactory> predicates, //路由器匹配规则
RouteDefinitionLocator routeDefinitionLocator, //路由定义信息加载器
ConfigurationService configurationService) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
gatewayFilters, properties, configurationService);
}
7. RouteDefinitionRouteLocator对象实例化构建
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
List<RoutePredicateFactory> predicates,
List<GatewayFilterFactory> gatewayFilterFactories,
GatewayProperties gatewayProperties,
ConfigurationService configurationService) {
this.routeDefinitionLocator = routeDefinitionLocator;
this.configurationService = configurationService;
initFactories(predicates); // 初始化路由匹配工厂
gatewayFilterFactories.forEach( //加载生成过滤器的工厂
factory -> this.gatewayFilterFactories.put(factory.name(), factory));
this.gatewayProperties = gatewayProperties;
}
a. initFactories(predicates) 初始化路由匹配工厂
主要是将路由匹配规则转换为断言对象,用来对每个请求的url进行判断是否使用该规则下的路由地址
private void initFactories(List<RoutePredicateFactory> predicates) {
predicates.forEach(factory -> {
String key = factory.name();
if (this.predicates.containsKey(key)) {
//打印日志
}
this.predicates.put(key, factory);
});
}
this.predicates的存储最后结果如下图,加载多种路由工厂
在我们的配置匹配规则时,会有类似于Path=/test/**前面的Path或者其它的,它是来决定使用哪个工厂来生成断言对象的。不同的规则有不同的方式。具体的路由匹配规则在下一章进行分析。
b. gatewayFilterFactories.forEach( factory -> this.gatewayFilterFactories.put(factory.name(), factory));
加载所有可以选择的过滤器
spring:
cloud:
gateway:
routes:
- id: test
uri: http://localhost:8081
predicates:
- Path=/test/**
- Query=param, value
filters: 过滤器
- AddRequestHeader=key,value
加载过滤器,gatewayFilterFactories是自动装配的bean,也都在GatewayAutoConfiguration中进行装配,gateway提供了很多可以在路由配置加的过滤器,例如:AddRequestHeader=key,value。
8. RouteDefinitionRouteLocator对象中getRoutes() 获取路由器
a. 这个方法主要上将配置文件中的路由定义信息转为可操作的路由对象
@Override
public Flux<Route> getRoutes() {
// 通过定义信息转为路由对象集Route
// routeDefinitionLocator.getRouteDefinitions路由的定义信息
// 重点在convertToRoute方法中
Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions()
.map(this::convertToRoute);
// 路由配置错误是否只是打印日志
if (!gatewayProperties.isFailOnRouteDefinitionError()) {
// instead of letting error bubble up, continue
routes = routes.onErrorContinue((error, obj) -> {
if (logger.isWarnEnabled()) {
logger.warn("RouteDefinition id " + ((RouteDefinition) obj).getId()
+ " will be ignored. Definition has invalid configs, "
+ error.getMessage());
}
});
}
// 打印配置的所有路由ID日志
return routes.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition matched: " + route.getId());
}
return route;
});
}
Flux可以理解为java8中提供的Stream类,包含的是0-n个对象,可以对包含的对象做批量处理,像在Stream中一样做过滤、排序、映射…
b. Route对象
public class Route implements Ordered {
// 路由ID
private final String id;
// 路由目标地址
private final URI uri;
// 路由加载顺序
private final int order;
// 该路由下的断言,也就是将匹配规则转为断言对象,是否执行该路由
private final AsyncPredicate<ServerWebExchange> predicate;
// 过滤器
private final List<GatewayFilter> gatewayFilters;
// 元数据
private final Map<String, Object> metadata;
}
predicate对象实际上就是一组规约对象,对一次请求进行判断是否使用该路由,这个对象实际上是包含一组,我们在配置路由规则时可以配置多个,所以需要所有的都匹配成功了才使用当前路由
c. convertToRoute方法
将路由定义信息转为路由对象
private Route convertToRoute(RouteDefinition routeDefinition) {
//获取路由下的匹配规则断言,具体实现可以看<路由匹配规则> 之后章节讲解
AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
//获取过滤器,包括gateway下的统一配置过滤器和路由下单独配的过滤器,之后章节详细讲解
List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
return Route.async(routeDefinition).asyncPredicate(predicate)
.replaceFilters(gatewayFilters).build();
}
d. 获取过滤器
配置文件
spring:
cloud:
gateway:
default-filters: // 默认过滤器(全局过滤器)
- AddRequestHeader=key,value
routes:
- id: test
uri: http://localhost:8081
predicates:
- Path=/test/**
- Query=param, value
filters: // 路由器下特定过滤器
- AddRequestHeader=key,value
order: 1
获取过滤器细节
private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
List<GatewayFilter> filters = new ArrayList<>();
// 获取全局过滤器,所有路由器共享
if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
filters.addAll(loadGatewayFilters(DEFAULT_FILTERS,
new ArrayList<>(this.gatewayProperties.getDefaultFilters())));
}
// 获取路由器下特定过滤器
if (!routeDefinition.getFilters().isEmpty()) {
filters.addAll(loadGatewayFilters(routeDefinition.getId(),
new ArrayList<>(routeDefinition.getFilters())));
}
//过滤器排序
AnnotationAwareOrderComparator.sort(filters);
return filters;
}
/** 获取指定过滤器的执行逻辑列表 */
List<GatewayFilter> loadGatewayFilters(String id,
List<FilterDefinition> filterDefinitions) {
ArrayList<GatewayFilter> ordered = new ArrayList<>(filterDefinitions.size());
for (int i = 0; i < filterDefinitions.size(); i++) {
FilterDefinition definition = filterDefinitions.get(i);
// 从所有的过滤器中找到需要那个过滤器
GatewayFilterFactory factory = this.gatewayFilterFactories
.get(definition.getName());
// 校验抛异常,省略。。。
// 构建过滤器所需参数
Object configuration = this.configurationService.with(factory)
.name(definition.getName())
.properties(definition.getArgs())
.eventFunction((bound, properties) -> new FilterArgsEvent(
// TODO: why explicit cast needed or java compile fails
RouteDefinitionRouteLocator.this, id, (Map<String, Object>) properties))
.bind();
// 有些过滤器需要路由的ID
if (configuration instanceof HasRouteId) {
HasRouteId hasRouteId = (HasRouteId) configuration;
hasRouteId.setRouteId(id);
}
// 通过指定的过滤工厂生成该路由的过滤器
GatewayFilter gatewayFilter = factory.apply(configuration);
// 如果得到的过滤器没有排序,则进行给排序设置
if (gatewayFilter instanceof Ordered) {
ordered.add(gatewayFilter);
}
else {
ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
}
}
//将过滤器集合返回
return ordered;
}
10. 总结
至此,加载路由配置的功能已经完成,可以通过RouteDefinitionRouteLocator的getRoutes()方法获取到所有的路由器列表Flux,它可以对应该请求进行校验是否匹配某个路由规则,并且依次执行全局过滤器以及可选过滤器