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-06
Java ThreadLocal詳解_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
ThreadLocal,很多地方叫做線程本地變量,也有些地方叫做線程本地存儲(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

