spring cloud gateway 的执行流程

spring cloud gateway 流程:

spring cloud gateway 官网的流程图

具体执行流程:

  1. DispatcherHandler :接收到请求后匹配 HandlerMapping ,此处会匹配到 RoutePredicateHandlerMapping ;
  2. RoutePredicateHandlerMapping :匹配 Route ;
  3. FilteringWebHandler :获取 Route 的 GatewayFilter 列表,创建 GatewayFilterChain 来处理请求。

DispatcherHandler

请求分发处理器,是WebFlux的访问入口。看这似曾相识的样子,没错,对应到Spring MVC中,跟它承担类似作用的,就是DispatcherServlet。DispatcherHander也和DispatcherServlet的请求处理流程类似:


public class DispatcherHandler implements WebHandler, ApplicationContextAware {
    private List<HandlerMapping> handlerMappings;
    private List<HandlerAdapter> handlerAdapters;
    private List<HandlerResultHandler> resultHandlers;

        // 1. 初始化时从BeanFactory分别拉取类型为:HandlerMapping、HandlerAdapter、HandlerResultHandler
        //    的所有Bean,填充到:handlerMappings、handlerAdapters、resultHandlers
    protected void initStrategies(ApplicationContext context) {
        Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                context, HandlerMapping.class, true, false);

        ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());
                // 对所有的HandlerMapping排序,实现按优先级选择HandlerMapping使用的关键
                // 因为HandlerMapping的作用是判断一个请求究竟适不适用于它所有对应的Handler
                // 最终是通过Handler去处理一个请求的,所以假设我们有一组HandlerMapping
                // 这时来了个请求,我们就先把HandlerMapping按getOrder()拿到的优先级排序
                // 然后依次做HandlerMapping.getHandler(request);
                // 如果这个过程是顺序的,或者说同步的,那么第一次有某个HandlerMapping
                // 拿到了Handler的话,说明它就是我们需要的Mr Right,方法可以直接返回这个了。
                // 其实在Spring MVC里 DispatcherServlet就是通过 for if != null return
                // 这样去顺序取HandlerMapping尝试获取Handler的。
                // 但是在Spring WebFlux里,Reactor编程将这个过程异步化了:
                // 通过concatMap()将所有的HandlerMapping异步getHandler(request);
                // 它不保证每个HandlerMapping执行getHandler的顺序,但保证最终得到的
                // 一组结果,是按原先HandlerMapping的顺序排好的,所以只用next()来获取
                // 第一个Handler,就得到了符合HandlerMapping优先级的Handler,得到了
                // 与同步执行同样的结果。假设HandlerMapping比较多,或者每个HandlerMapping
                // 执行getHandler()的时间比较久,那么异步是有优势的,它相当于是把这个过程
                // 并行化了。但也并非一定总有优势,要并行就需要分配多个线程去做任务,也涉及到
                // 最终结果的归并。多个线程会占用额外的cpu资源,与其他任务竞争时间片,CPU调度
                // 线程造成上下文切换消耗也增加了。并且每个getHandler的线程计算结果是需要维护
                // 对应的优先级的,因为最终合并结果需要保持不改变顺序。
        AnnotationAwareOrderComparator.sort(mappings);
        this.handlerMappings = Collections.unmodifiableList(mappings);

        Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                context, HandlerAdapter.class, true, false);

        this.handlerAdapters = new ArrayList<>(adapterBeans.values());
        AnnotationAwareOrderComparator.sort(this.handlerAdapters);

        Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
                context, HandlerResultHandler.class, true, false);

        this.resultHandlers = new ArrayList<>(beans.values());
        AnnotationAwareOrderComparator.sort(this.resultHandlers);
    }

        // 2.遍历handlerMappings,获取对应的WebHandler
    @Override
    public Mono<Void> handle(ServerWebExchange exchange) {
        if (this.handlerMappings == null) {
            return createNotFoundError();
        }
        return Flux.fromIterable(this.handlerMappings)
                                // a.此处会匹配到RoutePredicateHandlerMapping
                                // b.RoutePredicateHandlerMapping匹配请求对应的 Route
                                // c.返回FilteringWebHandler
                .concatMap(mapping -> mapping.getHandler(exchange))
                .next()
                .switchIfEmpty(createNotFoundError())
                                // d.执行Handler
                .flatMap(handler -> invokeHandler(exchange, handler))
                                // e.处理result
                .flatMap(result -> handleResult(exchange, result));
    }

        // 3.执行Handler,得到HandlerResult
    private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
        if (this.handlerAdapters != null) {
            for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
                if (handlerAdapter.supports(handler)) {
                    return handlerAdapter.handle(exchange, handler);
                }
            }
        }
        return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
    }

        // 5.处理返回结果
    private Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {
        return getResultHandler(result).handleResult(exchange, result)
                .checkpoint("Handler " + result.getHandler() + " [DispatcherHandler]")
                .onErrorResume(ex ->
                        result.applyExceptionHandler(ex).flatMap(exResult -> {
                            String text = "Exception handler " + exResult.getHandler() +
                                    ", error=\"" + ex.getMessage() + "\" [DispatcherHandler]";
                            return getResultHandler(exResult).handleResult(exchange, exResult).checkpoint(text);
                        }));
    }

        // 4.根据Result类型,获取处理返回结果的Handler
    private HandlerResultHandler getResultHandler(HandlerResult handlerResult) {
        if (this.resultHandlers != null) {
            for (HandlerResultHandler resultHandler : this.resultHandlers) {
                if (resultHandler.supports(handlerResult)) {
                    return resultHandler;
                }
            }
        }
        throw new IllegalStateException("No HandlerResultHandler for " + handlerResult.getReturnValue());
    }
}
上面的dispatcherHandler的流程简单点说:先初始换那些handlerMapping、handlerAdater、resultAdater,从中遍历了handlerMapping,最终它找到RoutePredicateHandlerMapping,就是开始第二步骤。

RoutePredicateHandlerMapping

  • 通过路由定位器获取全部路由(RouteLocator)
  • 通过路由的谓语(Predicate)过滤掉不可用的路由信息
  • 查找到路由信息后将路由信息设置当上下文环境中(GATEWAY_ROUTE_ATTR)
  • 返回gatway自定的webhandler(FilteringWebHandler)

执行他的核心方法:

@Override
    protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
        // don't handle requests on management port if set and different than server port
        if (this.managementPortType == DIFFERENT && this.managementPort != null
                && exchange.getRequest().getURI().getPort() == this.managementPort) {
            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<Route, Mono<?>>) r -> {
                    exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
                    if (logger.isDebugEnabled()) {
                        logger.debug(
                                "Mapping [" + getExchangeDesc(exchange) + "] to " + r);
                    }

                    exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
                    return Mono.just(webHandler);
                }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
                    exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
                    if (logger.isTraceEnabled()) {
                        logger.trace("No RouteDefinition found for ["
                                + getExchangeDesc(exchange) + "]");
                    }
                })));
    }


protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
        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());
                    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.<Route>empty().log("noroute"))
                .next()
                // TODO: error handling
                .map(route -> {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Route matched: " + route.getId());
                    }
                    validateRoute(route, exchange);
                    return route;
                });

        /*
         * TODO: trace logging if (logger.isTraceEnabled()) {
         * logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }
         */
    }
看lookupRoute方法就知道去找相应的Route了,是根据routeId去找的。找到相应的route,再返回FilteringWebHandler。

FilteringWebHandler 过滤web请求处理

她的本职工作是将GlobalFilter(全局过滤器)、GatewayFilter(一般来说是我们自己配置的,当然也默认内置了一些,也内含了自己在application配置文件中配的defaultFilter[如果有陪置的话]),放到同一个List里进行优先级排序,生成一个过滤器链。执行过滤器链,就能顺序执行链中保存的所有Filter。

public class FilteringWebHandler implements WebHandler {
    // 所有全局过滤器
    private final List<GatewayFilter> globalFilters;

    public FilteringWebHandler(List<GlobalFilter> globalFilters) {
        // 1.初始化时加载所有全局过滤器,将他们适配为GatewayFilter类型,方便等会做合并
        this.globalFilters = loadFilters(globalFilters);
    }
    // 适配器模式,通过两次套娃把GlobalFilter封装成GatewayFilter类型
    private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
    return filters.stream().map(filter -> {
            // 2.Adapter持有GlobalFilter的引用
        GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
        if (filter instanceof Ordered) {
        int order = ((Ordered) filter).getOrder();
                // 3.OrderedGatewayFilter再持有一个Adapter
                // 其实这里搞的挺无语的,那我直接返回Adapter又不是不能用,毕竟接口相同
                // 说白了,还是跟职责隔离、设计解耦有关
                // 就好比有时我们会纠结干嘛不直接把路由配置读成Route是类似的
                // 因为我们的思维模式,就是快点达到目的,而忽略了扩展性
                // 假设现在我们需要有一个服务提供多个接口,包括了Filter接口的功能
                // 那么,我们还是直接在Adapter上加接口,再持有一个跟它功能无关的工具人干活?
                // 那职责就完全乱套了,我们大可以这样:
                // Kunkun implements ISing,IJump,IRap,IBasketball {
                //     private ISing singAdapter;
                //     private IJump jumpAdapter;
                //     private IRap rapAdapter;
                //     private IBasketball basketballAdapter;
                // }
                // 那么坤坤就会唱、跳、rap、篮球了
        return new OrderedGatewayFilter(gatewayFilter, order);
        }
        return gatewayFilter;
        }).collect(Collectors.toList());
}

public Mono<Void> handle(ServerWebExchange exchange) {
    // 1.从exchange里拿到第二步匹配好的Route(匹配到后塞进去的)
    Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
    // 2.通过这个Route拿到它对应的GatewayFilter列表
    List<GatewayFilter> gatewayFilters = route.getFilters();
    // 3.把前面已经适配为GatewayFilter的全局过滤器拿过来,初始化一个总的过滤器列表
    List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
    // 4.然后把从Route里拿到的过滤器列表也塞进去合并
    combined.addAll(gatewayFilters);
    // 5.对总的过滤器列表按优先级排序
    AnnotationAwareOrderComparator.sort(combined);
    // 6.通过这个总的过滤器列表构造一个过滤器链来执行过滤逻辑
    return new DefaultGatewayFilterChain(combined).filter(exchange);
}
所以总结就是将globalFilter和route的filters结合在一块,形成一个过滤器链,之后请求就去逐个执行过滤器链的过滤器。

参考文章:

spring cloud gateway 专题收录1

spring cloud gateway 专题收录2

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

推荐阅读更多精彩内容