Java源碼解析之Gateway請求轉(zhuǎn)發(fā)
Gateway請求轉(zhuǎn)發(fā)
本期我們主要還是講解一下Gateway,上一期我們講解了一下Gateway中進行路由轉(zhuǎn)發(fā)的關(guān)鍵角色,過濾器和斷言是如何被加載的,上期鏈接://www.dbjr.com.cn/article/211824.htm
好了我們廢話不多說,開始今天的Gateway請求轉(zhuǎn)發(fā)流程講解,為了在講解源碼的時候,以防止大家可能會迷糊,博主專門畫了一下源碼流程圖,鏈接地址://www.dbjr.com.cn/article/211824.htm
上一期我們已經(jīng)知道了相關(guān)類的加載,今天直接從源碼開始,大家可能不太了解webflux和reactor這種響應(yīng)式編程,畢竟不是主流,我們一直用的都是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學(xué)習(xí)了。
Mono.empty();創(chuàng)建一個空Mono對象;
Mono.just(**);創(chuàng)建一個**元素的對象;
Mono.then(**);在最后執(zhí)行,相當(dāng)于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; }); }
//關(guān)鍵的代碼在這里 private Route convertToRoute(RouteDefinition routeDefinition) { //這兩步才會跟上一章節(jié)講解的如何加載斷言與過濾器有關(guān)聯(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,好了路由生成也選擇完畢了,我們應(yīng)該知道改請求是否符合我們配置的過濾器了,因為過濾器還沒用上,斷言只負責(zé)了選擇哪一個路由生效。
//我們看下一個主流程的方法 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); } //形成過濾器鏈,開始調(diào)用filter進行過濾。這里剩下的我們就不講解,跟spring配置的過濾器鏈調(diào)用流程是一樣的 return new DefaultGatewayFilterChain(combined).filter(exchange); }
至此,我們的請求流程基本完事了,我們再來看看幾個主要的全局過濾器配置。LoadBalancerClientFilter:負責(zé)獲取服務(wù)器ip的過濾器,NettyRoutingFilter:負責(zé)轉(zhuǎn)發(fā)我們請求的過濾器。
這里主要講解Gateway流程,關(guān)于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ā)送請求獲取響應(yīng) 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的主要請求轉(zhuǎn)發(fā)的流程,像webflux這種我們沒有精力學(xué)習(xí)的,可以暫時略過,畢竟也不是主流。我們今天最后總結(jié)一下。首先在Gateway這兩章的點,項目啟動時加載斷言與過濾器->接收請求時添加配置文件中的路由配置并生成路由對象->找到符合斷言的路由->除了個人配置的過濾器聯(lián)合全局過濾器生成過濾器鏈,并逐步過濾知道所有調(diào)用完成。
其中我們主要分析了兩個主要的全局過濾器:LoadBalancerClientFilter:負責(zé)獲取服務(wù)器ip的過濾器,NettyRoutingFilter:負責(zé)轉(zhuǎn)發(fā)我們請求的過濾器。
到此這篇關(guān)于Java源碼解析之Gateway請求轉(zhuǎn)發(fā)的文章就介紹到這了,更多相關(guān)Gateway請求轉(zhuǎn)發(fā)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java使用內(nèi)存數(shù)據(jù)庫ssdb的步驟
這篇文章主要介紹了java使用內(nèi)存數(shù)據(jù)庫ssdb的步驟,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-12-12java使用CollectionUtils工具類判斷集合是否為空方式
這篇文章主要介紹了java使用CollectionUtils工具類判斷集合是否為空方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02Java?ArrayList實現(xiàn)班級信息管理系統(tǒng)
這篇文章主要為大家詳細介紹了Java?ArrayList實現(xiàn)班級信息管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-02-02Java中使用HttpPost發(fā)送form格式的請求實現(xiàn)代碼
在Java中使用HttpPost發(fā)送form格式的請求,可以使用Apache HttpClient庫來實現(xiàn),這篇文章主要介紹了Java中使用HttpPost發(fā)送form格式的請求,本文給大家展示示例代碼,需要的朋友可以參考下2023-08-08基于Jenkins自動打包并部署docker環(huán)境的操作過程
這篇文章主要介紹了基于Jenkins自動打包并部署docker環(huán)境,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08ZooKeeper官方文檔之Java客戶端開發(fā)案例翻譯
網(wǎng)上有很多ZooKeeper的java客戶端例子,我也看過很多,不過大部分寫的都不好,有各種問題。兜兜轉(zhuǎn)轉(zhuǎn)還是覺得官方給的例子最為經(jīng)典,在學(xué)習(xí)之余翻譯下來,供朋友們參考2022-01-01Maven工程搭建spring boot+spring mvc+JPA的示例
本篇文章主要介紹了Maven工程搭建spring boot+spring mvc+JPA的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01