Spring Cloud Gateway(五):路由定位器 RouteLocator

本文基于 spring cloud gateway 2.0.1

1、简介

直接 获取 路 由 的 方法 是 通过 RouteLocator 接口 获取。 同样, 该 顶 级 接口 有多 个 实现 类,

RouteLocator 路由定位器,顾名思义就是用来获取路由的方法。该路由定位器为顶级接口有多个实现类,如类图所示,本节会对其实现类一一进行介绍。

路由定位器

通过类图可知,路由定位器接口有三种实现方法:

  • RouteDefinitionRouteLocator 基于路由定义的定位器
  • CachingRouteLocator 基于缓存的路由定位器
  • CompositeRouteLocator 基于组合方式的路由定位器

2、RouteLocator 路由定位器

与上节学习的路由定义定位器接口类似,RouteLocator 路由定位器只有一个 getRoutes 方法,用来获取路由信息。

public interface RouteLocator {//获取路由对象Flux<Route> getRoutes();
}

2.1、Route 路由对象

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;-----------------------省略-------------------------
}

3、 RouteDefinitionRouteLocator 基于路由定义的定位器

3.1、初始化

RouteDefinitionRouteLocator 构造函数有多个参数:路由定义定位器、路由断言工厂、网关过滤器及网关配置对象。 根据传入的参数,设置 routeDefinitionLocator 和 网关配置,并初始化路由断言 和 网关过滤器。 RouteDefinitionRouteLocator 的实现方式是基于路由定义来获取路由,它实现了 RouteLocator 接口,用来获取路由信息。

public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {protected final Log logger = LogFactory.getLog(getClass());private final RouteDefinitionLocator routeDefinitionLocator;private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();private final GatewayProperties gatewayProperties;private final SpelExpressionParser parser = new SpelExpressionParser();private BeanFactory beanFactory;private ApplicationEventPublisher publisher;public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,List<RoutePredicateFactory> predicates,List<GatewayFilterFactory> gatewayFilterFactories,GatewayProperties gatewayProperties) {//设置路由定义定位器this.routeDefinitionLocator = routeDefinitionLocator;//初始化路由断言工厂initFactories(predicates);//初始化网关过滤器gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory));this.gatewayProperties = gatewayProperties;}@Autowiredprivate Validator validator;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher publisher) {this.publisher = publisher;}private void initFactories(List<RoutePredicateFactory> predicates) {predicates.forEach(factory -> {String key = factory.name();if (this.predicates.containsKey(key)) {this.logger.warn("A RoutePredicateFactory named "+ key+ " already exists, class: " + this.predicates.get(key)+ ". It will be overwritten.");}this.predicates.put(key, factory);if (logger.isInfoEnabled()) {logger.info("Loaded RoutePredicateFactory [" + key + "]");}});}--------------------------------省略---------------------------------
}
  • 此种方式的路由获取是通过 RouteDefinitionRouteLocator 获取 RouteDefinition 并将路由定义转换成路由对象
  • 这里的routeDefinitionLocator是CompositeRouteDefinitionLocator,它组合了InMemoryRouteDefinitionRepository、PropertiesRouteDefinitionLocator、DiscoveryClientRouteDefinitionLocator三个RouteDefinitionLocator。
  • PropertiesRouteDefinitionLocator是直接使用GatewayProperties的getRoutes()获取,其是通过spring.cloud.gateway.routes配置得来。

3.2、RouteDefinition 转换成 Route 的流程

![image](路由转换流程图

// RouteDefinitionRouteLocator.java

@Overridepublic Flux<Route> getRoutes() {return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute)//TODO: error handling.map(route -> {if (logger.isDebugEnabled()) {logger.debug("RouteDefinition matched: " + route.getId());}return route;});/* TODO: trace loggingif (logger.isTraceEnabled()) {logger.trace("RouteDefinition did not match: " + routeDefinition.getId());}*/}private Route convertToRoute(RouteDefinition routeDefinition) {AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);return Route.async(routeDefinition).asyncPredicate(predicate).replaceFilters(gatewayFilters).build();}
  • getRoutes() :根据传入的 RouteDefinitionLocator 获取路由定义对象,使用map方法将每个 RouteDefinition 转换为 Route。

  • RouteDefinitionLocator#convertToRoute :是具体的转换方法,转换过程中涉及到路由断言 和 网关过滤器的处理,最后构建为Route 对象。

  • 此处网关过滤器处理包括两种,一种是默认过滤器,作用于所有路由;一种是指定路由的自定义过滤器。首先获取默认过滤器,根据过滤器名称获取对应的过滤器,最终转换成有优先级的OrderedGatewayFilter。

3.2.1、convertToRoute##combinePredicates

combinePredicates主要是对找出来的predicate进行and操作

private AsyncPredicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {List<PredicateDefinition> predicates = routeDefinition.getPredicates();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);}return predicate;}@SuppressWarnings("unchecked")private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route, PredicateDefinition predicate) {RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());if (factory == null) {throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());}Map<String, String> args = predicate.getArgs();if (logger.isDebugEnabled()) {logger.debug("RouteDefinition " + route.getId() + " applying "+ args + " to " + predicate.getName());}Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);Object config = factory.newConfig();ConfigurationUtils.bind(config, properties,factory.shortcutFieldPrefix(), predicate.getName(), validator);if (this.publisher != null) {this.publisher.publishEvent(new PredicateArgsEvent(this, route.getId(), properties));}return factory.applyAsync(config);}

3.2.2、convertToRoute##getFilters

getFilters 主要是利用loadGatewayFilters获取filter,使用AnnotationAwareOrderComparator进行排序
loadGatewayFilters利用工厂方法,使用GatewayFilterFactory根据config 获取具体的GatewayFilter实例

@SuppressWarnings("unchecked")private List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) {List<GatewayFilter> filters = filterDefinitions.stream().map(definition -> {GatewayFilterFactory factory = this.gatewayFilterFactories.get(definition.getName());if (factory == null) {throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName());}Map<String, String> args = definition.getArgs();if (logger.isDebugEnabled()) {logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName());}Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);Object configuration = factory.newConfig();ConfigurationUtils.bind(configuration, properties,factory.shortcutFieldPrefix(), definition.getName(), validator);GatewayFilter gatewayFilter = factory.apply(configuration);if (this.publisher != null) {this.publisher.publishEvent(new FilterArgsEvent(this, id, properties));}return gatewayFilter;}).collect(Collectors.toList());ArrayList<GatewayFilter> ordered = new ArrayList<>(filters.size());for (int i = 0; i < filters.size(); i++) {GatewayFilter gatewayFilter = filters.get(i);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<>();//TODO: support option to apply defaults after route specific filters?if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {filters.addAll(loadGatewayFilters("defaultFilters",this.gatewayProperties.getDefaultFilters()));}if (!routeDefinition.getFilters().isEmpty()) {filters.addAll(loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));}AnnotationAwareOrderComparator.sort(filters);return filters;}

4、 CachingRouteLocator 基于缓存的路由定位器

public class CachingRouteLocator implements RouteLocator {private final RouteLocator delegate;private final Flux<Route> routes;private final Map<String, List> cache = new HashMap<>();public CachingRouteLocator(RouteLocator delegate) {this.delegate = delegate;routes = CacheFlux.lookup(cache, "routes", Route.class).onCacheMissResume(() -> this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE));}@Overridepublic Flux<Route> getRoutes() {return this.routes;}/*** Clears the routes cache* @return routes flux*/public Flux<Route> refresh() {this.cache.clear();return this.routes;}@EventListener(RefreshRoutesEvent.class)/* for testing */ void handleRefresh() {refresh();}
}

基于缓存的路由定位器比较简单和缓存路由定义定位器比较类似,只需要调用 RouteLocator# getRoutes 即可获取路由。

根据传入的路由定位器获取路由信息并存储到缓存中。通过监听 RefreshRoutesEvent 事件刷新缓存的路由信息。

5、 CompositeRouteLocator 基于组合方式的路由定位器

public class CompositeRouteLocator implements RouteLocator {private final Flux<RouteLocator> delegates;public CompositeRouteLocator(Flux<RouteLocator> delegates) {this.delegates = delegates;}@Overridepublic Flux<Route> getRoutes() {return this.delegates.flatMap(RouteLocator::getRoutes);}
}

组合方式的路由定位器,将实现 RouteLocator 接口的路由定位器组合在一起,提供获取路由的统一入口。

6、小结

RouteLocator 接口用于获取路由信息,其有三个实现类

  • RouteDefinitionRouteLocator
  • CompositeRouteLocator
  • CachingRouteLocator

最终使用的是CachingRouteLocator,它包装了CompositeRouteLocator,而CompositeRouteLocator则组合了RouteDefinitionRouteLocator。

RouteDefinitionRouteLocator 与 RouteDefinitionLocator比较容易混淆,前者是一个RouteLocator(路由定位器),后者是一个RouteDefinitionLocator(路由定义定位器),前者的 RouteDefinitionRouteLocator 主要从后者获取路由定义信息。

转载于:https://www.cnblogs.com/liukaifeng/p/10055868.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/248952.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CommonJS,AMD,CMD区别 - 郑星阳 - ITeye博客

CommonJS&#xff0c;AMD&#xff0c;CMD区别 博客分类&#xff1a; seajs和requirejs JavaScript zccst转载 学得比较晕&#xff0c;再次看commonjs&#xff0c;amd, cmd时好像还是没完全弄清楚&#xff0c;今天再整理一下&#xff1a; commonjs是用在服务器端的&#xff…

739. Daily Temperatures

根据每日 气温 列表&#xff0c;请重新生成一个列表&#xff0c;对应位置的输入是你需要再等待多久温度才会升高的天数。如果之后都不会升高&#xff0c;请输入 0 来代替。 例如&#xff0c;给定一个列表 temperatures [73, 74, 75, 71, 69, 72, 76, 73]&#xff0c;你的输出应…

【NOIP2018】DAY2T2——填数游戏(轮廓线状压的dp?搜索打表)

描述 小 D 特别喜欢玩游戏。这一天&#xff0c;他在玩一款填数游戏。 这个填数游戏的棋盘是一个n m的矩形表格。玩家需要在表格的每个格子中填入一个数字&#xff08;数字 0 或者数字 1&#xff09;&#xff0c;填数时需要满足一些限制。 下面我们来具体描述这些限制。 为了方…

Mysql中遇到的错误

Caused by: java.sql.SQLException: Unknown system variable ‘tx_isolation’ 这种错误是因为数据库版本新的但是mysql的jar包是旧的&#xff0c;所以导入最新的mysqljar包 注意实体类和数据库字段的映射关系&#xff0c;实体类中使用驼峰式的命名规则&#xff0c;大写的字母…

Express 入门之Router - worldtree_keeper的专栏 - CSDN博客

要了解Router我们需要先知道到Application&#xff0c;首先&#xff0c;每一个express实例本身内部就内建了router&#xff0c;所以我们先从简单的下手&#xff0c;先使用application&#xff1b;另外这里我们只选择get方法&#xff0c;作为我们Router.Method, 之所以使用get是…

rest测试定义

1.为什么要做接口测试&#xff1a; 1.因为很多系统关联都是基于接口实现的&#xff0c;接口测试可以将系统复杂的系统关联进行简化 2.接口工程比较单一&#xff0c;能够比较好的进行测试覆盖&#xff0c;也相对容易实现自动化持续集成 3.接口相对于界面功能 &#xff0c;会更底…

团队开发进度报告9

&#xff08;1&#xff09;站立会议 &#xff08;2&#xff09;任务面板 &#xff08;3&#xff09;具体内容 昨天&#xff1a;完成了界面控件按钮的设置问题&#xff1a;PHP数据处理&#xff0c;如何实现在线数据交互问题今天&#xff1a;hbuilder后台环境搭建 转载于:https:/…

nodejs+express整合kindEditor实现图片上传 - 木子丰咪咕晶 - 开源中国

kindEditor官网上中提供了ASP,ASP.NET,JSP相关的整合应用,http://kindeditor.net/docs/upload.html可以参照实现nodejs的整合,发现实用nodejs更简单 环境: unbuntu 14.10 nodejs 0.10.35 express 4.11.2 formidable 1.0.16 kindEditor 4.1.10 webStorm 8 1.通过IDE或终端创建…

基于springboot多模块项目使用maven命令打成war包放到服务器上运行的问题

首先&#xff0c;大家看到这个问题&#xff0c;可能并不陌生&#xff0c;而且脑子里第一映像就是使用mava中的clear package 或者 clear install进行打包&#xff0c;然后在项目中的target文件夹下面找到xxx.war&#xff0c;将这个war包放到外置的tomcat服务器下的webapps下面&…

Kafka学习笔记(3)----Kafka的数据复制(Replica)与Failover

1. CAP理论 1.1 Cosistency(一致性) 通过某个节点的写操作结果对后面通过其他节点的读操作可见。 如果更新数据后&#xff0c;并发访问的情况下可立即感知该更新&#xff0c;称为强一致性 如果允许之后部分或全部感知不到该更新&#xff0c;称为弱一致性。 若在之后的一段时间&…

H5页面随机数字键盘支付页面

H5页面随机数字键盘支付页面 有个H5支付的业务需要随机数字的键盘 参考了下文&#xff1a;https://blog.csdn.net/Mr_Smile2014/article/details/52473351 做了一些小修改&#xff1a; 在原有的基础上&#xff0c;增加了一些按键反馈的效果。 每个按键加上边框。 最终效果&…

expressjs路由和Nodejs服务器端发送REST请求 - - ITeye博客

Nodejs创建自己的server后&#xff0c;我们如果需要从客户端利用ajax调用别的服务器端的数据API的接口&#xff0c;这时候出现了ajax跨域问题。 一种是利用在客户端解决跨域问题 这种方案大家可以去网上查查 另一种方案是在服务器端去请求别的服务器&#xff0c;然后将数据再…

Jmeter操作mysql数据库测试

1. 选中线程组鼠标点击右键添加-->配置元件-->JDBC Connection Configuration&#xff1b; 2. DataBase Connection Configuration配置 Variable Name&#xff1a;配置元件的的所有配置所保存的变量&#xff0c;自定义变量名称(不能使用mysql作为变量名&#xff0c;多个…

axios发送自定义请求头的跨域解决

前端发送来的axios请求信息 this.$axios.request({ url:http://127.0.0.1:8001/pay/shoppingcar/, method:post, headers:{ authenticate:a073b3dabbb140e8b9d28debb6a356a1 # 自定义的请求头部信息键值对, }, # 接上,这种key也算是一种请求头,需要加入django中间件内…

前端“智能”静态资源管理 - Onebox - 博客园

前端“智能”静态资源管理 模块化/组件化开发&#xff0c;仅仅描述了一种开发理念&#xff0c;也可以认为是一种开发规范&#xff0c;倘若你认可这规范&#xff0c;对它的分治策略产生了共鸣&#xff0c;那我们就可以继续聊聊它的具体实现了。 很明显&#xff0c;模块化/组件化…

【转】几张图看懂列式存储

几张图看懂列式存储 转载于:https://www.cnblogs.com/apeway/p/10870211.html

hive -e和hive -f的区别(转)

大家都知道&#xff0c;hive -f 后面指定的是一个文件&#xff0c;然后文件里面直接写sql&#xff0c;就可以运行hive的sql&#xff0c;hive -e 后面是直接用双引号拼接hivesql&#xff0c;然后就可以执行命令。 但是&#xff0c;有这么一个东西&#xff0c;我的sql当中有一个s…

我们是如何做好前端工程化和静态资源管理 - 無雄 - 博客园

我们是如何做好前端工程化和静态资源管理 随着互联网的发展&#xff0c;我们的业务也日益变得更加复杂且多样化起来&#xff0c;前端工程师也不再只是做简单的页面开发这么简单&#xff0c;我们需要面对的十分复杂的系统性问题&#xff0c;例如&#xff0c;业务愈来愈复杂&…

Mybatis-plus之RowBounds实现分页查询

物理分页和逻辑分页 物理分页&#xff1a;直接从数据库中拿出我们需要的数据&#xff0c;例如在Mysql中使用limit。 逻辑分页&#xff1a;从数据库中拿出所有符合要求的数据&#xff0c;然后再从这些数据中拿到我们需要的分页数据。 优缺点 物理分页每次都要访问数据库&#xf…

常见的6种JavaScript设计模式

常见的6种JavaScript设计模式 构造函数模式 /*** 构造一个动物的函数 */ function Animal(name, color){this.name name;this.color color;this.getName function(){return this.name;} } // 实例一个对象 var cat new Animal(猫, 白色); console.log( cat.getName() );工…