博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring cloud gateway 源码解析(2)动态路由
阅读量:6229 次
发布时间:2019-06-21

本文共 10008 字,大约阅读时间需要 33 分钟。

先看看gateway是怎么获取我们配置的路由:

在gateway启动时,GatewayAutoConfiguration帮我们注册了一系列beans

@Bean    @Primary    public RouteDefinitionLocator routeDefinitionLocator(List
routeDefinitionLocators) { //这里注入所有RouteDefinitionLocator,我们在配置文件里配置的路由就是通过子类PropertiesRouteDefinitionLocator来完成的, //可以参考InMemoryRouteDefinitionRepository实现自己的RouteDefinition的存储,RouteDefinition会在后面转换成Route return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators)); } //接着看CompositeRouteDefinitionLocator类里的获取路由定义方法: public Flux
getRouteDefinitions() { //即轮训所有RouteDefinitionLocator的市现率调用getRouteDefinitions,这样就把所有RouteDefinition整合到一起了 return this.delegates.flatMap(RouteDefinitionLocator::getRouteDefinitions); } //接着看GatewayAutoConfiguration,刚才的bean RouteDefinitionLocator 作为参数注入到routeDefinitionLocator @Bean public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List
GatewayFilters, List
predicates, RouteDefinitionLocator routeDefinitionLocator) { return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties); } //看RouteDefinitionRouteLocator类:public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware { ······ public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, List
predicates, List
gatewayFilterFactories, GatewayProperties gatewayProperties) { this.routeDefinitionLocator = routeDefinitionLocator; //RoutePredicateFactory放到map里,去掉RoutePredicateFactory后缀,所以配置时就可以写成 //predicates: // - Path=/abc/* //的形式而不用写完整的PathRoutePredicateFactory initFactories(predicates); //把bean名字里的GatewayFilterFactory去掉,所以我们配置文件里配置时也要把GatewayFilterFactory去掉,不然会找不到 gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory)); this.gatewayProperties = gatewayProperties; } ······ @Override public Flux
getRoutes() { return this.routeDefinitionLocator.getRouteDefinitions() .map(this::convertToRoute)//获取所有的RouteDefinition后转为Route //TODO: error handling .map(route -> { if (logger.isDebugEnabled()) { logger.debug("RouteDefinition matched: " + route.getId()); } return route; }); } private Route convertToRoute(RouteDefinition routeDefinition) { //获取所有匹配规则,所有的规则都满足才走对应Route AsyncPredicate
predicate = combinePredicates(routeDefinition); //获取全部过滤器 List
gatewayFilters = getFilters(routeDefinition); return Route.async(routeDefinition) .asyncPredicate(predicate) .replaceFilters(gatewayFilters) .build(); } private List
getFilters(RouteDefinition routeDefinition) { List
filters = new ArrayList<>(); //把全局过滤器添加到当前路由,loadGatewayFilters调用GatewayFilterFactory里的apply配置类里面的静态Config类,并且把没实现Ordered接口的类用OrderedGatewayFilter代理一下,方便下面排序 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; } private AsyncPredicate
combinePredicates(RouteDefinition routeDefinition) { //获取路由的判断条件,比如我们大多是根据url判断,用的是PathRoutePredicateFactory List
predicates = routeDefinition.getPredicates(); AsyncPredicate
predicate = lookup(routeDefinition, predicates.get(0)); for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) { AsyncPredicate
found = lookup(routeDefinition, andPredicate); predicate = predicate.and(found);//如果有多个匹配规则就要满足所有的才可以 } //返回的结果会在RoutePredicateHandlerMapping类里使用apply方法调用 return predicate; } @SuppressWarnings("unchecked") private AsyncPredicate
lookup(RouteDefinition route, PredicateDefinition predicate) { RoutePredicateFactory
factory = this.predicates.get(predicate.getName()); if (factory == null) { throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName()); } Map
args = predicate.getArgs(); if (logger.isDebugEnabled()) { logger.debug("RouteDefinition " + route.getId() + " applying " + args + " to " + predicate.getName()); } Map
properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory); //获取RoutePredicateFactory实现类里的静态Config 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)); } //这里实际调用的是ServerWebExchangeUtils类里的toAsyncPredicate方法,并配置的config return factory.applyAsync(config); }} // 把刚才的bean RouteDefinitionRouteLocator作为参数注入到routeLocators @Bean @Primary public RouteLocator cachedCompositeRouteLocator(List
routeLocators) { //这里CompositeRouteLocator与上面的CompositeRouteDefinitionLocator类似,也是遍历routeLocators调用getRoutes方法整合所有Route return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators))); } //接着看CachingRouteLocator类 public class CachingRouteLocator implements RouteLocator { private final RouteLocator delegate; private final Flux
routes; private final Map
cache = new HashMap<>(); public CachingRouteLocator(RouteLocator delegate) { this.delegate = delegate; routes = CacheFlux.lookup(cache, "routes", Route.class) //每次请求都会获取routes,每当拿缓存又找不到的时候(即我们修改了路由),就会调用上面CompositeRouteLocator的getRoutes方法重新获取路由 .onCacheMissResume(() -> this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE)); } ······· public Flux
refresh() { this.cache.clear();//清掉路由缓存 return this.routes; } @EventListener(RefreshRoutesEvent.class) /* for testing */ void handleRefresh() {//更新路由用的是spring事件机制 refresh(); }} //接着看GatewayAutoConfiguration,刚才的bean CachingRouteLocator作为参数注入到RouteLocator @Bean public RoutePredicateHandlerMapping routePredicateHandlerMapping( FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) { return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties, environment); }//接着看RoutePredicateHandlerMappingpublic class RoutePredicateHandlerMapping extends AbstractHandlerMapping { private final FilteringWebHandler webHandler; private final RouteLocator routeLocator; private final Integer managmentPort; public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) { this.webHandler = webHandler; this.routeLocator = routeLocator; if (environment.containsProperty("management.server.port")) { managmentPort = new Integer(environment.getProperty("management.server.port")); } else { managmentPort = null; } //这里设置了顺序,使得网关路由的匹配在requestmapping和handlefunction之后 setOrder(1); setCorsConfigurations(globalCorsProperties.getCorsConfigurations()); } @Override//方法在源码解析(1)中说的AbstractHandlerMapping类中获取Handler时调用 protected Mono
getHandlerInternal(ServerWebExchange exchange) { // don't handle requests on the management port if set if (managmentPort != null && exchange.getRequest().getURI().getPort() == managmentPort.intValue()) { //不处理特定配置端口的路由,返回empty即不用网关路由的handler,AbstractHandlerMapping继续找下一个 return Mono.empty(); } exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName()); return lookupRoute(exchange)//查找路由 // .log("route-predicate-handler-mapping", Level.FINER) //name this .flatMap((Function
>) r -> { exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR); if (logger.isDebugEnabled()) { logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r); } //把Route放到exchange的属性了,后面有几个过滤器用到了 exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r); //返回构造该类时传入的FilteringWebHandler,之后的流程就会跟上一篇里说的走下去了 return Mono.just(webHandler); }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> { //找不到匹配的路由的话进到这里,之后基本就是404了 exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR); if (logger.isTraceEnabled()) { logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]"); } }))); } ······ protected Mono
lookupRoute(ServerWebExchange exchange) { //调用上面提到的CachingRouteLocator的getRoutes方法获取所有Routes return this.routeLocator .getRoutes() //individually filter routes so that filterWhen error delaying is not a problem .concatMap(route -> Mono .just(route) .filterWhen(r -> {//过滤出符合的路由 // add the current route we are testing exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId()); //调用上面说的ServerWebExchangeUtils类里的toAsyncPredicate方法返回的lambda //里面会调用Predicate的实现类(都是lambda)的test方法返回true或者false return r.getPredicate().apply(exchange); }) //instead of immediately stopping main flux due to error, log and swallow it .doOnError(e -> logger.error("Error applying predicate for route: "+route.getId(), e)) .onErrorResume(e -> Mono.empty()) ) // .defaultIfEmpty() put a static Route not found // or .switchIfEmpty() // .switchIfEmpty(Mono.
empty().log("noroute")) .next()//过滤出一个后就停止了 //TODO: error handling .map(route -> { if (logger.isDebugEnabled()) { logger.debug("Route matched: " + route.getId()); } validateRoute(route, exchange); return route; }); } ·····}

说了那么多终于把网关匹配路由的流程说完了,如果上面都看明白的话,动态路由就好办了

网关已经把InMemoryRouteDefinitionRepository注册成bean(也可以参考这个类自己实现RouteDefinitionRepository接口),我们把它当作个service注入到controller,
前端把RouteDefinition用json的格式post过来,我们调用InMemoryRouteDefinitionRepository的save或者delete方法,再用spring的事件触发RefreshRoutesEvent事件来刷新路由就行了,等下次请求的时候就可以拿到新的路由配置了
顺序是:
1.RoutePredicateHandlerMapping 的lookupRoute方法,由于路由刷新事件把路由缓存清了,所以重新获取
2.CompositeRouteLocator 的getRoutes方法遍历所有RouteLocator实现类的getRoutes方法
3.RouteDefinitionRouteLocator 的getRoutes方法里重新获取了所有的路由定义,也就把我们刚刚用事件添加的路由也获取了

转载地址:http://tnxna.baihongyu.com/

你可能感兴趣的文章
python 曲线拟合求参数_Python:查找任意曲线的拟合参数数量
查看>>
python批量生成图片_python日常实用技能:如何利用Python批量生成任意尺寸的图片...
查看>>
python爱好者社区公众号历史文章合集_GitHub - thinkingpy/weixin_crawler: 高效微信公众号历史文章和阅读数据爬虫powered by scrapy...
查看>>
ranger安装hbase插件_ranger的配置与使用
查看>>
mysql模板文件_MySQL 配置文件模板
查看>>
mysql连接数据库测试连接成功_java连接mysql数据库及测试是否连接成功的方法
查看>>
1×pbs缓冲液配方_PBS缓冲液配方.doc
查看>>
mysql qadir_MySQL 及 SQL 注入
查看>>
python 播放视频 ftp_视频在通过Python ftplib上传时出现损坏
查看>>
python3 image_python docker快速入门3制作image
查看>>
python代码写名字_【python】【名字】是什么?,Pythonname
查看>>
python调用浏览器生成dom_1分钟快速生成用于网页内容提取的xslt
查看>>
tolua unity 报错_关于LUA+Unity开发_toLua篇【二】
查看>>
java this函数_java this 用法详解
查看>>
java怎么封装表单数据_java 对form表单数据进行封装list
查看>>
java 当前工作目录是指_java取得当前工作目录
查看>>
the java jive_Java Jive
查看>>
hadoop上传文件java_hadoop入门之通过java代码实现将本地文件上传到hadoop的文件系统...
查看>>
放苹果 java_用Java代码模拟实现:一个人不断往箱子里放苹果,另一个人不
查看>>
java ftp 判断目录存在_java判断ftp目录是否存在的方法
查看>>