最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java源碼解析之Gateway請求轉發(fā)

 更新時間:2021年06月24日 11:00:19   作者:努力的小雨  
今天給大家?guī)淼氖顷P于Java的相關知識,文章圍繞著Gateway請求轉發(fā)展開,文中有非常詳細介紹及代碼示例,需要的朋友可以參考下

Gateway請求轉發(fā)

本期我們主要還是講解一下Gateway,上一期我們講解了一下Gateway中進行路由轉發(fā)的關鍵角色,過濾器和斷言是如何被加載的,上期鏈接://m.fzitv.net/article/211824.htm

好了我們廢話不多說,開始今天的Gateway請求轉發(fā)流程講解,為了在講解源碼的時候,以防止大家可能會迷糊,博主專門畫了一下源碼流程圖,鏈接地址://m.fzitv.net/article/211824.htm

上一期我們已經知道了相關類的加載,今天直接從源碼開始,大家可能不太了解webflux和reactor這種響應式編程,畢竟不是主流,我們一直用的都是spring MVC,沒事,我們主要講解流程,不做過多的講解。

大家先看下面的代碼,我們今天主要的代碼入口就是這里:

public Mono<Void> handle(ServerWebExchange exchange) {
        if (logger.isDebugEnabled()) {
            ServerHttpRequest request = exchange.getRequest();
            logger.debug("Processing " + request.getMethodValue() + " request for [" + request.getURI() + "]");
        }
        if (this.handlerMappings == null) {
            return Mono.error(HANDLER_NOT_FOUND_EXCEPTION);
        }
        return Flux.fromIterable(this.handlerMappings)
                .concatMap(mapping -> mapping.getHandler(exchange))
                .next()
                .switchIfEmpty(Mono.error(HANDLER_NOT_FOUND_EXCEPTION))
                .flatMap(handler -> invokeHandler(exchange, handler))
                .flatMap(result -> handleResult(exchange, result));
    }

第一步,我們先來看一看幾個主要的類及其方法,F(xiàn)lux 表示的是包含 0 到 N 個元素的異步序列,Mono 表示的是包含 0 或者 1 個元素的異步序列,記住Flux是多個元素集合,Mono 是單個元素集合就很好理解以后的源碼了,以下方法注釋是博主為了大家好理解而寫的,具體實際的意義還是需要大家自行Google學習了。

Mono.empty();創(chuàng)建一個空Mono對象;

Mono.just(**);創(chuàng)建一個**元素的對象;

Mono.then(**);在最后執(zhí)行,相當于spring的aop后置通知一樣

開始我們的第一步解析:mapping.getHandler(exchange);本方法主要做的是獲取路由,我們繼續(xù)看一看底層源碼:

getHandler

getHandlerInternal

//這里返回的是單個對象
    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;
                });


    }

我們現(xiàn)在看看Route對象是怎么在getRoutes()創(chuàng)建的。

public 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;
                });

    }
//關鍵的代碼在這里
    private Route convertToRoute(RouteDefinition routeDefinition) {
    //這兩步才會跟上一章節(jié)講解的如何加載斷言與過濾器有關聯(lián),大家可以自行查看底層源碼是如何查出來的對象的
        AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
        List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
    //終于生成了路由對象
        return Route.async(routeDefinition)
                .asyncPredicate(predicate)
                .replaceFilters(gatewayFilters)
                .build();
    }

這里大家要記住getHandlerInternal方法,生成了Mono.just(webHandler),仔細看webHandler是FilteringWebHandler對象,以后用到這個WebHandler,好了路由生成也選擇完畢了,我們應該知道改請求是否符合我們配置的過濾器了,因為過濾器還沒用上,斷言只負責了選擇哪一個路由生效。

//我們看下一個主流程的方法
    private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
        if (this.handlerAdapters != null) {
            for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
                if (handlerAdapter.supports(handler)) {
                //這里走的是SimpleHandlerAdapter,可以自己debug發(fā)現(xiàn),也可以去找自動配置類找,這里就不講解了
                    return handlerAdapter.handle(exchange, handler);
                }
            }
        }
        return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
    }
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
        WebHandler webHandler = (WebHandler) handler;
        //讓大家記住的那個FilteringWebHandler類,終于在這里起作用了。我們這回可以看看過濾器是如何起作用的
        Mono<Void> mono = webHandler.handle(exchange);
         return mono.then(Mono.empty());//過濾器處理完后,開始處理mono.then方法
    }
public Mono<Void> handle(ServerWebExchange exchange) {
        Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
        List<GatewayFilter> gatewayFilters = route.getFilters();//我們路由自己配置的過濾器
        //加載全局過濾器
        List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
        combined.addAll(gatewayFilters);
        //TODO: needed or cached?
        AnnotationAwareOrderComparator.sort(combined);
        //排序
        if (logger.isDebugEnabled()) {
            logger.debug("Sorted gatewayFilterFactories: "+ combined);
        }
        //形成過濾器鏈,開始調用filter進行過濾。這里剩下的我們就不講解,跟spring配置的過濾器鏈調用流程是一樣的
        return new DefaultGatewayFilterChain(combined).filter(exchange);
    }

至此,我們的請求流程基本完事了,我們再來看看幾個主要的全局過濾器配置。LoadBalancerClientFilter:負責獲取服務器ip的過濾器,NettyRoutingFilter:負責轉發(fā)我們請求的過濾器。

這里主要講解Gateway流程,關于Ribbon的代碼我們就不做主要講解了

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
        String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
        //所以要加上lb前綴,才會走該過濾器
        if (url == null || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
            return chain.filter(exchange);
        }
        //preserve the original url
        addOriginalRequestUrl(exchange, url);

        log.trace("LoadBalancerClientFilter url before: " + url);
        //選擇實例
        final ServiceInstance instance = choose(exchange);

        ......
        return chain.filter(exchange);
    }

看主要代碼即可,非必要的看了也暈。

public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        .......
        //通過httpClient發(fā)送請求獲取響應
        Mono<HttpClientResponse> responseMono = this.httpClient.request(method, url, req -> {
            final HttpClientRequest proxyRequest = req.options(NettyPipeline.SendOptions::flushOnEach)
                    .headers(httpHeaders)
                    .chunkedTransfer(chunkedTransfer)
                    .failOnServerError(false)
                    .failOnClientError(false);

            if (preserveHost) {
                String host = request.getHeaders().getFirst(HttpHeaders.HOST);
                proxyRequest.header(HttpHeaders.HOST, host);
            }

            if (properties.getResponseTimeout() != null) {
                proxyRequest.context(ctx -> ctx.addHandlerFirst(
                        new ReadTimeoutHandler(properties.getResponseTimeout().toMillis(), TimeUnit.MILLISECONDS)));
            }

            return proxyRequest.sendHeaders() //I shouldn't need this
                    .send(request.getBody().map(dataBuffer ->
                            ((NettyDataBuffer) dataBuffer).getNativeBuffer()));
        });

        return responseMono.doOnNext(res -> {
        ...
        }

    }

我們今天主要看的是Gateway的主要請求轉發(fā)的流程,像webflux這種我們沒有精力學習的,可以暫時略過,畢竟也不是主流。我們今天最后總結一下。首先在Gateway這兩章的點,項目啟動時加載斷言與過濾器->接收請求時添加配置文件中的路由配置并生成路由對象->找到符合斷言的路由->除了個人配置的過濾器聯(lián)合全局過濾器生成過濾器鏈,并逐步過濾知道所有調用完成。

其中我們主要分析了兩個主要的全局過濾器:LoadBalancerClientFilter:負責獲取服務器ip的過濾器,NettyRoutingFilter:負責轉發(fā)我們請求的過濾器。

到此這篇關于Java源碼解析之Gateway請求轉發(fā)的文章就介紹到這了,更多相關Gateway請求轉發(fā)內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • java實現(xiàn)RedisTemplate操作哈希數(shù)據(jù)

    java實現(xiàn)RedisTemplate操作哈希數(shù)據(jù)

    RedisTemplate是Spring Data Redis提供的一個用于操作Redis的模板類,本文主要介紹了java實現(xiàn)RedisTemplate操作哈希數(shù)據(jù),具有一定的參考價值,感興趣的可以了解一下
    2024-09-09
  • 一篇文章帶你了解Java Stream流

    一篇文章帶你了解Java Stream流

    Stream流是數(shù)據(jù)渠道,用于操作數(shù)據(jù)源(集合、數(shù)組等)所生成的元素序列。這篇文章主要介紹了Java8新特性Stream流的相關資料,需要的朋友參考下吧
    2021-08-08
  • JAVA將中文轉換為拼音簡單實現(xiàn)方法

    JAVA將中文轉換為拼音簡單實現(xiàn)方法

    拼音轉換是中文處理的常見需求,TinyPinyin、HanLP、pinyin4j是常用的本地拼音轉換庫,各有特點,開發(fā)者可根據(jù)具體需求選擇合適的拼音轉換工具,需要的朋友可以參考下
    2024-10-10
  • 2021年最新Redis面試題匯總(3)

    2021年最新Redis面試題匯總(3)

    在程序員面試過程中redis相關的知識是常被問到的話題。這篇文章主要介紹了幾道Redis面試題,整理一下分享給大家,感興趣的小伙伴們可以參考一下
    2021-07-07
  • JPA @Basic單表查詢如何實現(xiàn)大字段懶加載

    JPA @Basic單表查詢如何實現(xiàn)大字段懶加載

    這篇文章主要介紹了JPA @Basic單表查詢如何實現(xiàn)大字段懶加載的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Mybatis-Plus實現(xiàn)SQL攔截器的示例

    Mybatis-Plus實現(xiàn)SQL攔截器的示例

    這篇文章主要介紹了Mybatis-Plus實現(xiàn)一個SQL攔截器,通過使用SQL攔截器,開發(fā)人員可以在執(zhí)行SQL語句之前或之后對其進行修改或記錄,從而更好地控制和優(yōu)化數(shù)據(jù)庫操作,對Mybatis-Plus?SQL攔截器相關知識感興趣的朋友一起看看吧
    2023-05-05
  • 20秒教你學會java?List函數(shù)排序操作示例

    20秒教你學會java?List函數(shù)排序操作示例

    這篇文章主要為大家介紹了20秒教你學會List函數(shù)排序操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09
  • Java棧和基礎隊列的實現(xiàn)詳解

    Java棧和基礎隊列的實現(xiàn)詳解

    這篇文章主要介紹了Java數(shù)據(jù)結構中的棧與隊列,在Java的時候,對于棧與隊列的應用需要熟練的掌握,這樣才能夠確保Java學習時候能夠有扎實的基礎能力。本文小編就來詳細說說Java中的棧與隊列,需要的朋友可以參考一下
    2022-02-02
  • Java集合Iterator迭代的實現(xiàn)方法

    Java集合Iterator迭代的實現(xiàn)方法

    這篇文章主要介紹了Java集合Iterator迭代接口的實現(xiàn)方法,非常不錯,具有參考借鑒家,對Java 結合iterator知識感興趣的朋友一起看看吧
    2016-08-08
  • 使用IDEA創(chuàng)建maven父子工程項目 (圖文)

    使用IDEA創(chuàng)建maven父子工程項目 (圖文)

    本文主要介紹了使用IDEA創(chuàng)建maven父子工程項目,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-04-04

最新評論

交口县| 霞浦县| 太仓市| 南开区| 利辛县| 松溪县| 乌兰浩特市| 无锡市| 华池县| 金阳县| 尉氏县| 克拉玛依市| 孝义市| 富顺县| 沛县| 遵化市| 卓资县| 五华县| 前郭尔| 喜德县| 洪雅县| 定南县| 望城县| 大悟县| 汶川县| 屏东县| 喀喇沁旗| 洪江市| 旺苍县| 武川县| 平罗县| 巫溪县| 南江县| 宣汉县| 沿河| 南部县| 大城县| 蓬安县| 阜新市| 曲水县| 三台县|