Spring-Cloud-Gateway之RoutePredicate

Spring-Cloud-Gateway路由选择是通过Predicate函数式接口进行判断当前路由是否满足给定条件。下来阅读Spring-Cloud-Gateway路由的Predicate构建流程以及选择路由的源码


首先看下JDK8中Predicate接口定义

Predicate<T> 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)。可以用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。add--与、or--或、negate--非

  • boolean test(T t); 判断
  • Predicate<T> and(Predicate<? super T> other) 接收一个Predicate类型,也就是将传入的条件和当前条件以并且(AND)的关系组合
  • Predicate<T> or(Predicate<? super T> other)接收一个Predicate类型,也就是将传入的条件和当前条件以或(OR)的关系组合

  1. 加载路由中的Predicate

在路由定位器中以及看到了通过路由定义转换路由方法,其中包含了通过谓语定义(PredicateDefinition)转换谓语(Predicate)的部分,在RouteDefinitionRouteLocator类中源码如下:

    /**
     * 返回组合的谓词
     * @param routeDefinition
     * @return
     */
    private Predicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
        //获取RouteDefinition中的PredicateDefinition集合
        List<PredicateDefinition> predicates = routeDefinition.getPredicates();

        Predicate<ServerWebExchange> predicate = lookup(routeDefinition, predicates.get(0));

        for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
            Predicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate);
            //返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑AND
            predicate = predicate.and(found);
        }

        return predicate;
    }
    
        /**
     * 获取一个谓语定义(PredicateDefinition)转换的谓语
     * @param route
     * @param predicate
     * @return
     */
    @SuppressWarnings("unchecked")
    private Predicate<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.apply(config);
    }
    
  1. 获取路由定义(routeDefinition)所有的谓语定位(PredicateDefinition)
  2. 以此根据谓语定义(PredicateDefinition)查找谓语对于的创建工厂(RoutePredicateFactory) 创建谓语
  3. 通过 Predicate<T>接口 and方法合并谓语集合返回一个新的复合谓语
  1. 使用路由Predicate判断路由是否可用

在Spring-Cloud-Gateway之请求处理流程里我们已经了解在handlerMapping中通过路由定位器获取所有路由,并过滤掉谓语判断失败的路由,最终获取满足条件的路由,下面再看下过滤的具体逻辑。

protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
        //通过路由定位器获取路由信息
        return this.routeLocator.getRoutes()
                .filter(route -> {
            exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, route.getId());
                    //返回通过谓语过滤的路由信息
                    return route.getPredicate().test(exchange);
                })
    }
  1. 通过routeLocator的getRoutes方法获取所有路由
  2. 在所有路由中通过filter方法匹配Predicate.test匹配的路由信息

通过上面1,2两步我们看到了整个路由的条件创建以及使用的地方以及流程,Spring-Cloud-Gateway通过Predicate接口完成简单条件组合以及判断。

接下来看通过RoutePredicateFactory创建PredicateSpring-Cloud-Gateway预制了很多RoutePredicateFactory使其可以通过简单的配置就可以创建出理想的Predicate

RoutePredicateFactory类图如下:


RoutePredicateFactoryUML.png

RoutePredicateFactory子类功能分类图:


PredicateFactory.png

  • MethodRoutePredicateFactory
/**
 * 请求方式(GET,POST,DEL,PUT)校验匹配创建工厂
 * @author Spencer Gibb
 */
public class MethodRoutePredicateFactory extends AbstractRoutePredicateFactory<MethodRoutePredicateFactory.Config> {

    public static final String METHOD_KEY = "method";
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            //获取当前请求的HttpMethod
            HttpMethod requestMethod = exchange.getRequest().getMethod();
            //校验请求HttpMethod与配置是否一致
            return requestMethod == config.getMethod();
        };
    }

    public static class Config {
        /**
         * http 请求Method
         */
        private HttpMethod method;

        public HttpMethod getMethod() {
            return method;
        }

        public void setMethod(HttpMethod method) {
            this.method = method;
        }
    }
}

配置列子

spring:
  cloud:
    gateway:
      routes:
      # =====================================
      - id: method_route
        uri: http://example.org
        predicates:
        - Method=GET
  1. Method=GET 会被解析成PredicateDefinition对象 (name =Method ,args= GET)
  2. 通过PredicateDefinition的Name找到MethodRoutePredicateFactory工厂
  3. 通过 PredicateDefinition 的args 创建Config对象(HttpMethod=GET)
  4. 通过 MethodRoutePredicateFactory工厂的apply方法传入config创建Predicate对象。

在此只对MethodRoutePredicateFactory做了描述和说明,其它的谓语工程基本与此一致。此时Spring-Cloud-Gateway的RoutePredicate所有逻辑代码都已经阅读完毕,对阅读过程做文档记录以便加深印象,后期翻阅

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,050评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,184评论 6 345
  • 前言 在微服务架构的系统中,我们通常会使用轻量级的消息代理来构建一个共用的消息主题让系统中所有微服务实例都连接上来...
    Chandler_珏瑜阅读 6,436评论 2 39
  • Spring-Cloud-Gateway路由信息是通过路由定位器RouteLocator加载以及初始化的接下来阅读...
    Mr_1214阅读 13,299评论 1 6
  • 凉夏雪莹阅读 132评论 0 1