本文基于 spring cloud gateway 2.0.1

1、简介

RouteDefinitionLocator 是路由定义定位器的顶级接口,它的主要作用就是读取路由的配置信息(org.springframework.cloud.gateway.route.RouteDefinition)。它有五种不同的实现类,如图

2、RouteDefinitionLocator

org.springframework.cloud.gateway.route.RouteDefinitionLocator ,路由定义定位器接口,只有一个方法,用来获取路由定义列表的方法。

public interface RouteDefinitionLocator {

	Flux<RouteDefinition> getRouteDefinitions();
}

通过 RouteDefinitionLocator 的类图,可以看出该接口有多个实现类:

  • PropertiesRouteDefinitionLocator:基于属性配置
  • DiscoveryClientRouteDefinitionLocator:基于服务发现
  • CompositeRouteDefinitionLocator:组合方式
  • CachingRouteDefinitionLocator:缓存方式
  • 其中还有一个接口 RouteDefinitionRepository 继承自RouteDefinitionLocator,用于对路由定义的操作(保存、删除路由定义)

2.1、RouteDefinition

RouteDefinition 作为GatewayProperties中的属性,在网关启动的时候读取配置文件中的相关配置信息

@Validated
public class RouteDefinition { @NotEmpty
private String id = UUID.randomUUID().toString(); @NotEmpty
@Valid
private List<PredicateDefinition> predicates = new ArrayList(); @Valid
private List<FilterDefinition> filters = new ArrayList(); @NotNull
private URI uri; private int order = 0; public RouteDefinition() {
} public RouteDefinition(String text) {
int eqIdx = text.indexOf(61);
if (eqIdx <= 0) {
throw new ValidationException("Unable to parse RouteDefinition text '" + text + "', must be of the form name=value");
} else {
this.setId(text.substring(0, eqIdx));
String[] args = StringUtils.tokenizeToStringArray(text.substring(eqIdx + 1), ",");
this.setUri(URI.create(args[0])); for(int i = 1; i < args.length; ++i) {
this.predicates.add(new PredicateDefinition(args[i]));
} }
}
----------------------省略----------------------------
}

在 RouteDefinition 中,主要有五个属性:

id:路由id,默认为uuid

predicates:PredicateDefinition 路由断言定义列表

filters:FilterDefinition 过滤器定义列表

uri:URI 转发地址

order:优先级

进入断言和路由器属性可以看到他们是一个 Map 数据结构,可以存放多个对应的 键值对数组

3、RouteDefinitionRepository & InMemoryRouteDefinitionRepository

RouteDefinitionRepository 接口中的方法用来对RouteDefinition进行增、删、查操作

public interface RouteDefinitionRepository extends RouteDefinitionLocator, RouteDefinitionWriter {
}
//读取路由定义信息
public interface RouteDefinitionLocator { Flux<RouteDefinition> getRouteDefinitions();
}
//对路由定会进行保存和删除操作
public interface RouteDefinitionWriter {
Mono<Void> save(Mono<RouteDefinition> route); Mono<Void> delete(Mono<String> routeId);
}

RouteDefinitionRepository 通过继承自 RouteDefinitionLocator、 RouteDefinitionWriter,封装了对路由定义信息的获取、增加、删除操作,在网关内置API端点接口时会用到这些操作。

InMemoryRouteDefinitionRepository 实现了 RouteDefinitionRepository 接口,基于内存的路由定义仓库,同时也是唯一提供的实现类。我们可以根据需要自定义扩展,存放到其它的存储介质中。

public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {
private final Map<String, RouteDefinition> routes = Collections.synchronizedMap(new LinkedHashMap()); public InMemoryRouteDefinitionRepository() {
} //保存路由定义到内存中
public Mono<Void> save(Mono<RouteDefinition> route) {
return route.flatMap((r) -> {
this.routes.put(r.getId(), r);
return Mono.empty();
});
}
//根据路由id从内存中删除指定路由定义
public Mono<Void> delete(Mono<String> routeId) {
return routeId.flatMap((id) -> {
if (this.routes.containsKey(id)) {
this.routes.remove(id);
return Mono.empty();
} else {
return Mono.defer(() -> {
return Mono.error(new NotFoundException("RouteDefinition not found: " + routeId));
});
}
});
} //获取内存中路由定义列表
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(this.routes.values());
}
}

4、PropertiesRouteDefinitionLocator 基于配置属性的路由定义定位器

从配置文件 yaml或properties中读取路由配置信息,如代码所示

public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator {
private final GatewayProperties properties; public PropertiesRouteDefinitionLocator(GatewayProperties properties) {
this.properties = properties;
} public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(this.properties.getRoutes());
}
}

PropertiesRouteDefinitionLocator 通过构造函数传入 GatewayProperties 对象,然后从该对象中读取路由配置信息

5、DiscoveryClientRouteDefinitionLocator 基于服务发现的路由定义定位器

该类通过服务发现组件从注册中心获取服务信息,此时路由定义的源就是配置中心

public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {
//服务发现客户端
private final DiscoveryClient discoveryClient;
//服务发现属性
private final DiscoveryLocatorProperties properties;
//路由id前缀
private final String routeIdPrefix; ------------------------------省略--------------------------------
}
//服务发现属性对象
@ConfigurationProperties("spring.cloud.gateway.discovery.locator")
public class DiscoveryLocatorProperties {
// 开启服务发现
private boolean enabled = false;
// 路由前缀,默认为 discoveryClient. getClass(). getSimpleName() + "_".
private String routeIdPrefix;
// SpEL 表达式,判断网关是否集成一个服务,默认为 true
private String includeExpression = "true";
// SpEL 表达式,为每个路由创建uri,默认为'lb://'+ serviceId
private String urlExpression = "'lb://'+ serviceId";
// 在 断言 和 过滤器 中使用小写 serviceId,默认为 false
private boolean lowerCaseServiceId = false;
//路由断言定义列表
private List<PredicateDefinition> predicates = new ArrayList();
//过滤器定义列表
private List<FilterDefinition> filters = new ArrayList();
------------------------------省略--------------------------------
}

在 DiscoveryLocatorProperties 定义了以上属性,要启用基于服务发现的路由定义定位器就必须设置

spring.cloud.gateway.discovery.locator.enabled= true

includeExpression 属性判断网关是否集成一个服务,默认为true,

根据 includeExpression 表达式,过滤不符合的 ServiceInstance。

DiscoveryClientRouteDefinitionLocator -> getRouteDefinitions()

@Override
public Flux<RouteDefinition> getRouteDefinitions() {
//对 includeExpression 和 urlExpression 的 表达式 处理
SpelExpressionParser parser = new SpelExpressionParser();
Expression includeExpr = parser.parseExpression(properties.getIncludeExpression());
Expression urlExpr = parser.parseExpression(properties.getUrlExpression()); Predicate<ServiceInstance> includePredicate;
if (properties.getIncludeExpression() == null || "true".equalsIgnoreCase(properties.getIncludeExpression())) {
includePredicate = instance -> true;
} else {
includePredicate = instance -> {
Boolean include = includeExpr.getValue(evalCtxt, instance, Boolean.class);
if (include == null) {
return false;
}
return include;
};
}
//通过注册中心查找服务组装路由定义信息
return Flux.fromIterable(discoveryClient.getServices())
.map(discoveryClient::getInstances)
.filter(instances -> !instances.isEmpty())
.map(instances -> instances.get(0))
// 根据 includeExpression 表达式,过滤不符合的 ServiceInstance
.filter(includePredicate)
.map(instance -> {
String serviceId = instance.getServiceId(); RouteDefinition routeDefinition = new RouteDefinition();
routeDefinition.setId(this.routeIdPrefix + serviceId);
String uri = urlExpr.getValue(evalCtxt, instance, String.class);
routeDefinition.setUri(URI.create(uri)); final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties); //添加配置的断言表达式
for (PredicateDefinition original : this.properties.getPredicates()) {
PredicateDefinition predicate = new PredicateDefinition();
predicate.setName(original.getName());
for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
predicate.addArg(entry.getKey(), value);
}
routeDefinition.getPredicates().add(predicate);
} //添加配置的过滤器
for (FilterDefinition original : this.properties.getFilters()) {
FilterDefinition filter = new FilterDefinition();
filter.setName(original.getName());
for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
filter.addArg(entry.getKey(), value);
}
routeDefinition.getFilters().add(filter);
} return routeDefinition;
});
} String getValueFromExpr(SimpleEvaluationContext evalCtxt, SpelExpressionParser parser, ServiceInstance instance, Map.Entry<String, String> entry) {
Expression valueExpr = parser.parseExpression(entry.getValue());
return valueExpr.getValue(evalCtxt, instance, String.class);
} private static class DelegatingServiceInstance implements ServiceInstance { final ServiceInstance delegate;
private final DiscoveryLocatorProperties properties; private DelegatingServiceInstance(ServiceInstance delegate, DiscoveryLocatorProperties properties) {
this.delegate = delegate;
this.properties = properties;
} @Override
public String getServiceId() {
if (properties.isLowerCaseServiceId()) {
return delegate.getServiceId().toLowerCase();
}
return delegate.getServiceId();
} }

从源码可以看出,getRouteDefinitions 方法通过服务发现客户端从注册中心获取服务信息,组装成RouteDefinition路由定义列表,并将配置中的路由断言和过滤应用到RouteDefinition 中

6、CachingRouteDefinitionLocator 基于缓存的路由定义定位器

缓存方式的路由定义定位器,通过传入路由定义定位器获取路由定义并缓存到本地。通过监听路由刷新时间RefreshRoutesEvent 来刷新本地缓存的路由定义信息

public class CachingRouteDefinitionLocator implements RouteDefinitionLocator {
//路由定义定位器
private final RouteDefinitionLocator delegate;
//路由定义信息
private final Flux<RouteDefinition> routeDefinitions;
//本地缓存集合
private final Map<String, List> cache = new HashMap(); public CachingRouteDefinitionLocator(RouteDefinitionLocator delegate) {
this.delegate = delegate;
this.routeDefinitions = CacheFlux.lookup(this.cache, "routeDefs", RouteDefinition.class).onCacheMissResume(() -> {
return this.delegate.getRouteDefinitions();
});
} public Flux<RouteDefinition> getRouteDefinitions() {
return this.routeDefinitions;
} //刷新本地缓存,先清空本地缓存再获取一份新的路由定义信息存储
public Flux<RouteDefinition> refresh() {
this.cache.clear();
return this.routeDefinitions;
}
//监听路由刷新事件,刷新本地缓存的路由定义信息
@EventListener({RefreshRoutesEvent.class})
void handleRefresh() {
this.refresh();
}
}

7、CompositeRouteDefinitionLocator 组合路由定义定位器

组合方式路由定义定位器使用组合模式进行实现,组合多个 RouteDefinitionLocator 的实现,为获取路由定义信息 getRouteDefinitions 提供统一入口,组合的逻辑很简单,通过传入的路由定义定位器作为代理,具体的路由定义实际上是由传入的路由定义定位器产生。

public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator {
private final Flux<RouteDefinitionLocator> delegates; public CompositeRouteDefinitionLocator(Flux<RouteDefinitionLocator> delegates) {
this.delegates = delegates;
} public Flux<RouteDefinition> getRouteDefinitions() {
return this.delegates.flatMap(RouteDefinitionLocator::getRouteDefinitions);
}
}

Spring Cloud Gateway(四):路由定义定位器 RouteDefinitionLocator的更多相关文章

  1. 通过Nacos动态刷新Spring Cloud Gateway的路由

    通过Nacos动态刷新Spring Cloud Gateway的路由 一.背景 二.解决方案 三.实现功能 四.实现步骤 1.网关服务的实现 1.pom文件 2.bootstrap.yml配置文件 3 ...

  2. springcloud3(五) spring cloud gateway动态路由的四类实现方式

    写这篇博客主要是为了汇总下动态路由的多种实现方式,没有好坏之分,任何的方案都是依赖业务场景需求的,现在网上实现方式主要有: 基于Nacos, 基于数据库(PosgreSQL/Redis), 基于Mem ...

  3. Spring Cloud Alibaba学习笔记(17) - Spring Cloud Gateway 自定义路由谓词工厂

    在前文中,我们介绍了Spring Cloud Gateway内置了一系列的路由谓词工厂,但是如果这些内置的路由谓词工厂不能满足业务需求的话,我们可以自定义路由谓词工厂来实现特定的需求. 例如有某个服务 ...

  4. spring cloud gateway网关路由分配

    1, 基于父工程,新建一个模块 2,pom文件添加依赖 <dependencies> <dependency> <groupId>org.springframewo ...

  5. Spring Cloud Alibaba学习笔记(16) - Spring Cloud Gateway 内置的路由谓词工厂

    Spring Cloud Gateway路由配置的两种形式 Spring Cloud Gateway的路由配置有两种形式,分别是路由到指定的URL以及路由到指定的微服务,在上文博客的示例中我们就已经使 ...

  6. Spring cloud gateway 如何在路由时进行负载均衡

    本文为博主原创,转载请注明出处: 1.spring cloud gateway 配置路由 在网关模块的配置文件中配置路由: spring: cloud: gateway: routes: - id: ...

  7. Spring Cloud Gateway(一):认识Spring Cloud Gateway

    1.Spring Cloud Gateway 简介 Spring Cloud Gateway 系列目录 Spring Cloud Gateway(一):认识Spring Cloud Gateway S ...

  8. Spring Cloud Gateway入坑记

    Spring Cloud Gateway入坑记 前提 最近在做老系统的重构,重构完成后新系统中需要引入一个网关服务,作为新系统和老系统接口的适配和代理.之前,很多网关应用使用的是Spring-Clou ...

  9. Spring Cloud Gateway服务网关

    原文:https://www.cnblogs.com/ityouknow/p/10141740.html Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gatewa ...

随机推荐

  1. kafka的安装及使用(单节点)

    介绍了linux环境下,kafka 服务的安装与配置 安装 jdk 环境 下载 kafka 源码包放到服务器,解压 开启 zookeeper 开启 kafka server 创建主题 开启生产者 开启 ...

  2. 十年感悟之 python之路

    本文由 简悦 SimpRead 转码, 原文地址 https://laisky.com/p/python-road/ Changelog: updated at 2019/9/6 一.概述 本文起源于 ...

  3. SD-定义定价用途的条件表(Condition Table)

    https://www.fenginfo.com/815.html 条件表(Condition Table)是SAP条件技术体系中最基础的数据源,它们是一群特定的数据表,表名是以特定字母开头 + 三位 ...

  4. STM8 定时器

    中断映射表 对应stm8_interrupt.c #pragma vector=1 __interrupt void TRAP_IRQHandler(void) { } #pragma vector= ...

  5. 阿里云服务器(Ubuntu16.04 64位)的使用

    购买阿里云服务器 1.打开阿里云官方网站,账号登录,选择产品中的云服务器 ECS 2.根据自身需求,选择合适的阿里云服务器系统,(1)点击一键购买,(2)选择地域,(3)根据自身需求,选择系统,这里选 ...

  6. Windows——Office使用激活工具激活后仍提示激活

    问题: Office使用激活工具激活后仍提示激活 分析: 造成该问题的原因通常是未删除操作系统预置Office导致的, 解决方案: 调出运行,输入regedit打开注册表编辑器, 依次打开   HKE ...

  7. 2.Git 结构

    1.Git 结构: 使用git add命令将写的代码暂存到暂存区:使用git commit命令将暂存区的代码提交到本地库: 2. Git 结构及其代码托管中心: workSpace:工作区(写代码). ...

  8. 20.Vue中获取DOM元素和组件

    1.获取DOM元素和组件:this.$refs

  9. Kubernetes 1.14发布:对Windows节点的生产级支持、Kubectl更新与持久本地卷通用版本已全面到来

    今天,我们高兴地宣布Kubernetes 1.14版本的正式亮相,这亦是我们在2019年当中进行的首次发布!Kubernetes 1.14版本由31项增强功能组成,具体包括:10项稳定版功能,12项b ...

  10. 三维视觉、SLAM方向全球顶尖实验室汇总

    本文作者 任旭倩,公众号:计算机视觉life,编辑成员 欧洲 英国伦敦大学帝国理工学院 Dyson 机器人实验室 http://www.imperial.ac.uk/dyson-robotics-la ...