Spring中Feign的調(diào)用流程詳解
Feign的調(diào)用流程
動(dòng)態(tài)代理的入口
前面已經(jīng)分析過(guò)了創(chuàng)建的代理是FeignInvocationHandler,那我們就打斷點(diǎn),停在它的反射方法上,看看到底做了什么。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("equals".equals(method.getName())) { try { Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null; return equals(otherHandler); } catch (IllegalArgumentException e) { return false; } } else if ("hashCode".equals(method.getName())) { return hashCode(); } else if ("toString".equals(method.getName())) { return toString(); } return dispatch.get(method).invoke(args); }
dispatch此處就是之前封裝的5個(gè)SynchronousMethodHandler方法的集合,這里更加方法去獲取,然后調(diào)用invoke方法。來(lái)到了SynchronousMethodHandler這個(gè)方法。
@Override public Object invoke(Object[] argv) throws Throwable { RequestTemplate template = buildTemplateFromArgs.create(argv); Options options = findOptions(argv); Retryer retryer = this.retryer.clone(); while (true) { try { return executeAndDecode(template, options); } catch (RetryableException e) { try { retryer.continueOrPropagate(e); } catch (RetryableException th) { Throwable cause = th.getCause(); if (propagationPolicy == UNWRAP && cause != null) { throw cause; } else { throw th; } } if (logLevel != Logger.Level.NONE) { logger.logRetry(metadata.configKey(), logLevel); } continue; } } }
第一行首先會(huì)創(chuàng)建出一個(gè)template的,它的結(jié)果如下圖:
最終把上面獲取到的2個(gè)變量帶到了executeAndDecode方法,這個(gè)方法才是執(zhí)行和解碼的方法。
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable { //這里的request就已經(jīng)把服務(wù)名給加上了,變成了一個(gè)具體的請(qǐng)求。 Request request = targetRequest(template); if (logLevel != Logger.Level.NONE) { logger.logRequest(metadata.configKey(), logLevel, request); } Response response; long start = System.nanoTime(); try { //來(lái)到了這里,無(wú)論是負(fù)載均衡還是請(qǐng)求響應(yīng)都是這邊完成的,那我們就點(diǎn)進(jìn)去看看。 response = client.execute(request, options); // ensure the request is set. TODO: remove in Feign 12 response = response.toBuilder() .request(request) .requestTemplate(template) .build(); } catch (IOException e) { if (logLevel != Logger.Level.NONE) { logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start)); } throw errorExecuting(request, e); } long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); if (decoder != null) return decoder.decode(response, metadata.returnType()); CompletableFuture<Object> resultFuture = new CompletableFuture<>(); asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response, metadata.returnType(), elapsedTime); try { if (!resultFuture.isDone()) throw new IllegalStateException("Response handling not done"); return resultFuture.join(); } catch (CompletionException e) { Throwable cause = e.getCause(); if (cause != null) throw cause; throw e; } }
Feign是如何實(shí)現(xiàn)負(fù)載均衡的
先進(jìn)入到前面的lbClient方法,返回一個(gè)FeignLoadBalancer,說(shuō)明這里和ribbon結(jié)合了。
private FeignLoadBalancer lbClient(String clientName) { return this.lbClientFactory.create(clientName); }
此處調(diào)用了一個(gè)create方法,那就進(jìn)去看看。
注意這里的factory,其實(shí)就是SpringClientFactory,從它里面獲取了lb,lb里面包含了注冊(cè)的服務(wù)清單,然后再把它放到本地的緩存當(dāng)中。
接著就會(huì)執(zhí)行,現(xiàn)在它的sumbit方法中打一個(gè)斷點(diǎn):
發(fā)現(xiàn)里面會(huì)執(zhí)行一個(gè)selectServer()方法,肯定是這個(gè)里面選擇了服務(wù)
在點(diǎn)進(jìn)去看看,于是就找到了ribbon中熟悉的方法了,就是這里選擇了那個(gè)服務(wù)
選擇好了那個(gè)服務(wù),就繼續(xù)放下走,開(kāi)始這邊拼接ip和url了
會(huì)把選擇的ip和后面請(qǐng)求的url都傳進(jìn)來(lái)。
在其父類(lèi)的reconstructURIWithServer方法中完成了拼接,如下圖:
后面就是請(qǐng)求響應(yīng)和解碼了
到此這篇關(guān)于Spring中Feign的調(diào)用流程詳解的文章就介紹到這了,更多相關(guān)Feign的調(diào)用流程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java中BigDecimal類(lèi)的構(gòu)造詳解及使用
這篇文章主要介紹了java中BigDecimal類(lèi)的構(gòu)造詳解及使用,Java在java.math包中提供的API類(lèi)BigDecimal,用來(lái)對(duì)超過(guò)16位有效位的數(shù)進(jìn)行精確的運(yùn)算,需要的朋友可以參考下2023-07-07最新hadoop安裝教程及hadoop的命令使用(親測(cè)可用)
這篇文章主要介紹了最新hadoop安裝教程(親測(cè)可用),本文主要講解了如何安裝hadoop、使用hadoop的命令及遇到的問(wèn)題解決,需要的朋友可以參考下2022-06-06Java ThreadLocal詳解_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
ThreadLocal,很多地方叫做線(xiàn)程本地變量,也有些地方叫做線(xiàn)程本地存儲(chǔ),本文會(huì)詳細(xì)的介紹一下,有興趣的可以了解一下2017-06-06利用SpringBoot解決多個(gè)定時(shí)任務(wù)阻塞的問(wèn)題
當(dāng)我們?cè)赟pring Boot應(yīng)用中使用多個(gè)定時(shí)任務(wù)時(shí),任務(wù)之間的阻塞可能是一個(gè)常見(jiàn)的問(wèn)題,這可能會(huì)因任務(wù)之間的依賴(lài)、執(zhí)行時(shí)間過(guò)長(zhǎng)或資源爭(zhēng)用等原因而發(fā)生,本文讓我們深入探討如何利用Spring Boot來(lái)解決多個(gè)定時(shí)任務(wù)阻塞的問(wèn)題,感興趣的小伙伴跟著小編一起來(lái)看看吧2024-01-01