route路由的配置加载
主要在sprng-cloud-gateway-server的route包定义路由相关的定义,构建和加载
![路由](https://fengzhenbing.github.io/img/picgo/image (2).png)
0.相关配置
- 通过springboot spi方式,springboot会启动spring.factories中配置的 org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.gateway.config.GatewayAutoConfiguration,\
org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration,\
...
1.路由定义
- 路由定义RouteDefinition
public class RouteDefinition {
private String id;
@NotEmpty
@Valid//fzb 断言定义数组
private List<PredicateDefinition> predicates = new ArrayList<>();
@Valid//fzb 过滤器定义数组
private List<FilterDefinition> filters = new ArrayList<>();
@NotNull//fzb 路由路径
private URI uri;
...
}
- 路由定义定位器
获取路由的定义,负责读取上述路由定义配置 RouteDefinition,最终会通过路由定义生成路由
public interface RouteDefinitionLocator {
//fzb 获取路由定义对象
Flux<RouteDefinition> getRouteDefinitions();
}
有以下实现:
![image (3)](https://fengzhenbing.github.io/img/picgo/image (3).png)
- CachingRouteDefinitionLocator: 它包装了CompositeRouteDefinitionLocator,缓存路由定义RouteDefinition列表
public class CachingRouteDefinitionLocator implements RouteDefinitionLocator, ApplicationListener {//fzb 事件监听RefreshRoutesEvent
private static final String CACHE_KEY = "routeDefs";
private final RouteDefinitionLocator delegate;
//fzb 路由定义flux
private final Flux<RouteDefinition> routeDefinitions;
//fzb 内存缓存RouteDefinition
private final Map<String, List> cache = new ConcurrentHashMap<>();
...
//fzb 监听到路由刷新事件,重新获取路由并缓存到cache
@Override
public void onApplicationEvent(RefreshRoutesEvent event) {
fetch().materialize().collect(Collectors.toList())
.doOnNext(routes -> cache.put(CACHE_KEY, routes)).subscribe();
}
… }
> * CompositeRouteDefinitionLocator:
> 遍历执行所有RouteDefinitionLocator的查找路由定义方法,将找到的路由定义合并; 组合多种 RouteDefinitionLocator 的实现,为 routeDefinitions提供统一入口
```java
public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator {
...
//fzb 组合多个路由定义定位器RouteDefinitionLocator
private final Flux<RouteDefinitionLocator> delegates;
// fzb 路由定义的id 生成器,默认UUID
private final IdGenerator idGenerator;
...
public CompositeRouteDefinitionLocator(Flux<RouteDefinitionLocator> delegates,
IdGenerator idGenerator) {
this.delegates = delegates;
this.idGenerator = idGenerator;// 路由定义的id 生成器,默认UUID
}
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
//遍历执行所有RouteDefinitionLocator的查找路由定义方法,将找到的路由定义合并
return this.delegates
.flatMapSequential(RouteDefinitionLocator::getRouteDefinitions)
.flatMap(routeDefinition -> {
if (routeDefinition.getId() == null) {
return randomId().map(id -> {
routeDefinition.setId(id);// 设置uuid
if (log.isDebugEnabled()) {
log.debug(
"Id set on route definition: " + routeDefinition);
}
return routeDefinition;
});
}
return Mono.just(routeDefinition);
});
}
...
}
- PropertiesRouteDefinitionLocator: 从配置文件(GatewayProperties 例如,YML / Properties 等 ) 读取RouteDefinition
//fzb 从GatewayProperties读取路由定义
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(this.properties.getRoutes());
}
- DiscoveryClientRouteDefinitionLocator 从注册中心如:Netflix Eureka, Consul 或 Zookeeper读取RouteDefinition
public DiscoveryClientRouteDefinitionLocator(ReactiveDiscoveryClient discoveryClient,
DiscoveryLocatorProperties properties) {
this(discoveryClient.getClass().getSimpleName(), properties);
//fzb 通过服务发现客户端 ,比如eureka客户端从注册中心拿到所有的服务实例
serviceInstances = discoveryClient.getServices()
.flatMap(service -> discoveryClient.getInstances(service).collectList());
}
将服务实例serviceInstances转为路由定义
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
SpelExpressionParser parser = new SpelExpressionParser();
Expression includeExpr = parser
.parseExpression(properties.getIncludeExpression());//fzb 扩展点,可以通过properties 配置其他spel
Expression urlExpr = parser.parseExpression(properties.getUrlExpression());
...
}
- RouteDefinitionRepository 从存储器(内存 / Redis / MySQL 等 )读取RouteDefinition,实现RouteDefinitionWriter接口; 提供删除和保存RouteDefinition的方法; 默认实现有:InMemoryRouteDefinitionRepository,没有RouteDefinitionRepository的实例,则默认用InMemoryRouteDefinitionRepository
public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {
private final Map<String, RouteDefinition> routes = synchronizedMap(
new LinkedHashMap<String, RouteDefinition>());//fzb 内存中用 同步map 存储
@Override// fzb 保存路由到内存中
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap(r -> {
if (StringUtils.isEmpty(r.getId())) {
return Mono.error(new IllegalArgumentException("id may not be empty"));
}
routes.put(r.getId(), r);//保存
return Mono.empty();
});
}
@Override// fzb 从内存中删除路由
public Mono<Void> delete(Mono<String> routeId) {
...
}
@Override// fzb 获取路由定义
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(routes.values());
}
}
- 自定义路由存储器,redis存储:RedisRouteDefinitionRepository https://github.com/fengzhenbing/spring-cloud-gateway-demo/blob/master/demo-gateway/src/main/java/org/fzb/demo/gateway/route/RedisRouteDefinitionRepository.java
2.路由
- 路由Route
public class Route implements Ordered {
private final String id;
//fzb 路由地址
private final URI uri;
//fzb 路由的优先级
private final int order;
private final AsyncPredicate<ServerWebExchange> predicate;
//fzb gatewayFilter列表
private final List<GatewayFilter> gatewayFilters;
//fzb 元数据
private final Map<String, Object> metadata;
...
}
- 路由定位器 RouteLocator 获取路由对象
public interface RouteLocator {
//fzb 获取路由对象
Flux<Route> getRoutes();
}
类似RouteDefinitionLocator三个实现:
- 缓存:CachingRouteLocator: 缓存路由,查找时使用CompositeRouteLocator去查找
- 组合: CompositeRouteLocator:使用多个RouteLocator查找路由并合并
- 单个的 RouteDefinitionRouteLocator:最终使用RouteDefinitionLocator查找路由定义并转为路由对象Route 单个路由的 filters = GlobalFilter + 默认GatewayFilter + 本路由配置的GatewayFilter
public class RouteDefinitionRouteLocator
implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {
...
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
List<RoutePredicateFactory> predicates,
List<GatewayFilterFactory> gatewayFilterFactories,
GatewayProperties gatewayProperties,
ConfigurationService configurationService) {
this.routeDefinitionLocator = routeDefinitionLocator;//fzb //设置路由定义定位器
this.configurationService = configurationService;
initFactories(predicates);//fzb //初始化路由断言工厂
gatewayFilterFactories.forEach(
factory -> this.gatewayFilterFactories.put(factory.name(), factory));//fzb //初始化网关过滤器
this.gatewayProperties = gatewayProperties;
}
...
//fzb 获取路由对象: 先获取RouteDefinition,再转为 Route
@Override
public Flux<Route> getRoutes() {
Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions()
.map(this::convertToRoute);//fzb RouteDefinition转为 Route
...
}
//fzb 路由定义转为 路由
private Route convertToRoute(RouteDefinition routeDefinition) {
//fzb 1,将本路由定义中各个断言 与运算 合并为一个 AsyncPredicate
AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
//fzb 2, 获取所有的默认的过滤器和本路由定义的过滤器
List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
// 组装Route
return Route.async(routeDefinition).asyncPredicate(predicate)
.replaceFilters(gatewayFilters).build();
}
//fzb 过滤器定义filterDefinitions加载为过滤器GatewayFilter
@SuppressWarnings("unchecked")
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());
if (factory == null) {
throw new IllegalArgumentException(
"Unable to find GatewayFilterFactory with name "
+ definition.getName());
}
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + id + " applying filter "
+ definition.getArgs() + " to " + definition.getName());
}
// @formatter:off
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();
// @formatter:on
// some filters require routeId
// TODO: is there a better place to apply this?
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;
}
private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
List<GatewayFilter> filters = new ArrayList<>();
//fzb 1,加上默认的过滤器
// TODO: support option to apply defaults after route specific filters?
if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
filters.addAll(loadGatewayFilters(DEFAULT_FILTERS,
new ArrayList<>(this.gatewayProperties.getDefaultFilters())));
}
//fzb 2,加上本路由配置的过滤器
if (!routeDefinition.getFilters().isEmpty()) {
filters.addAll(loadGatewayFilters(routeDefinition.getId(),
new ArrayList<>(routeDefinition.getFilters())));
}
//fzb 排序 按实现的Ordered接口
AnnotationAwareOrderComparator.sort(filters);
return filters;
}
//fzb combinePredicates主要是对找出来的predicate进行and操作
private AsyncPredicate<ServerWebExchange> combinePredicates(
RouteDefinition routeDefinition) {
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
if (predicates == null || predicates.isEmpty()) {
// this is a very rare case, but possible, just match all
return AsyncPredicate.from(exchange -> true);
}
AsyncPredicate<ServerWebExchange> predicate = lookup(routeDefinition,
predicates.get(0));
for (PredicateDefinition andPredicate : predicates.subList(1,
predicates.size())) {
AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition,
andPredicate);
predicate = predicate.and(found);//fzb and 各个断言 与 合并为一个 AsyncPredicate
}
return predicate;
}
...
}
类比
- Route -> RouteDefinition ->RouteDefinitionLocator -> xxxRouteDefinitionRepository
- Bean -> BeanDefinition -> BeanDefinitionRegistry -> DefaultListableBeanFactory#Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);