Spring Cloud OpenFeign調(diào)用流程

上一節(jié)給大家分享了Spring Cloud OpenFeign的啟動(dòng)流程,接下來(lái)給大家分享一下調(diào)用流程。話不多說(shuō),咱們直接開(kāi)始。
視頻:https://www.bilibili.com/video/BV1A84y1C7XD/
調(diào)用流程
xxxFeignClient
→ feign.ReflectiveFeign.FeignInvocationHandler#invoke
→ feign.InvocationHandlerFactory.MethodHandler#invoke
→ feign.SynchronousMethodHandler#invoke
→ feign.SynchronousMethodHandler#executeAndDecode
→ org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient#execute
→ feign.Client.Default#execute
→ feign.AsyncResponseHandler#handleResponse
動(dòng)態(tài)代理
feign.ReflectiveFeign.FeignInvocationHandler#invoke
public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
??return?dispatch.get(method).invoke(args);
}
這里說(shuō)一下 dispatch
屬性,它的類(lèi)型是Map<Method,?MethodHandler>
意思是,可以通過(guò)方法找到對(duì)應(yīng)的Handler,這樣就可以進(jìn)入到 SynchronousMethodHandler#invoke。
feign.SynchronousMethodHandler#executeAndDecode
從這個(gè)方法的名稱(chēng)也能看出來(lái),這個(gè)是執(zhí)行請(qǐng)求,并且實(shí)現(xiàn)解碼的功能,這是一個(gè)核心的方法。
負(fù)載均衡
org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient#execute
這個(gè)是實(shí)現(xiàn)均衡,實(shí)現(xiàn)將URL中服務(wù)名轉(zhuǎn)成 真實(shí)的IP。
下面我們看看它是如何被自動(dòng)注入的。
首先在 spring.factories
文件中,做了配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration
FeignLoadBalancerAutoConfiguration 中引入 DefaultFeignLoadBalancerConfiguration
//?Order?is?important?here,?last?should?be?the?default,?first?should?be?optional
//?see
//?https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
public?class?FeignLoadBalancerAutoConfiguration?{
}
new FeignBlockingLoadBalancerClient,并且注入到 Spring Bean 中
class?DefaultFeignLoadBalancerConfiguration?{
?
?
?
?public?Client?feignClient(LoadBalancerClient?loadBalancerClient,
???LoadBalancerClientFactory?loadBalancerClientFactory)?{
??return?new?FeignBlockingLoadBalancerClient(new?Client.Default(null,?null),?loadBalancerClient,
????loadBalancerClientFactory);
?}
}
Http請(qǐng)求
下面我們看看 feign 是如何實(shí)現(xiàn) Http 請(qǐng)求的。
feign.Client.Default#execute
public?Response?execute(Request?request,?Options?options)?throws?IOException?{
??HttpURLConnection?connection?=?convertAndSend(request,?options);
??return?convertResponse(connection,?request);
}
主要就是在這個(gè)方法中,默認(rèn)使用 jdk 實(shí)現(xiàn) http請(qǐng)求。
convertAndSend,這個(gè)方法做了兩件事,一是,打開(kāi) Http 連接,獲取到 HttpURLConnection
,并設(shè)置相關(guān)屬性;二是,如果有參數(shù),就通過(guò)輸出流(OutputStream
)寫(xiě)入?yún)?shù)。
convertResponse,這個(gè)方法返回的是 feign.Response
,我們它有哪些屬性:
public?final?class?Response?implements?Closeable?{
??private?final?int?status;
??private?final?String?reason;
??private?final?Map<String,?Collection<String>>?headers;
??private?final?Body?body;
??private?final?Request?request;
??private?final?ProtocolVersion?protocolVersion;
}
首先,這里實(shí)現(xiàn) Closeable 接口,所以必然有 close 方法,我們看一下:
public?void?close()?{
??Util.ensureClosed(body);
}
好了,明白了,body實(shí)際上是寫(xiě)入流(InputStream
)。
總結(jié)一下:這里實(shí)現(xiàn)了Http請(qǐng)求,上傳了參數(shù),或獲得了輸入流。
Http響應(yīng)處理
看完了請(qǐng)求,我們?cè)倩氐?feign.SynchronousMethodHandler#executeAndDecode,看下面的代碼
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;
}
這里是通過(guò) CompletableFuture,來(lái)裝配響應(yīng)結(jié)果。
feign.AsyncResponseHandler#handleResponse,這個(gè)方法就也就是處理Http響應(yīng)結(jié)果的入口。
比如要判斷狀態(tài)碼,獲取結(jié)果,關(guān)閉輸入流等。
響應(yīng)結(jié)果解碼
解碼流程如下:
feign.AsyncResponseHandler#decode
→ org.springframework.cloud.openfeign.support.ResponseEntityDecoder#decode
→ org.springframework.cloud.openfeign.support.SpringDecoder#decode
→ org.springframework.cloud.openfeign.support.SpringDecoder.FeignResponseAdapter#FeignResponseAdapter
→ org.springframework.web.client.HttpMessageConverterExtractor#extractData
→ org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter#read
為什么需要解碼呢?
http響應(yīng)的結(jié)果類(lèi)型是String,而我們需要的是一個(gè)對(duì)象,比如:
public?interface?IGoodsFeignClient?{
????
????ResultTemplate<ListTemplate<GoodsModel>>?list();
}
我是 Erwin Feng,一個(gè)專(zhuān)注于高質(zhì)量編程的開(kāi)發(fā)者。如果你對(duì)我內(nèi)容感興趣,可以關(guān)注我的微信公眾號(hào)【Erwin Feng】,可以第一時(shí)間收到更新推送!