日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

Feign源码解析5:loadbalancer

發布時間:2024/1/16 windows 40 coder
生活随笔 收集整理的這篇文章主要介紹了 Feign源码解析5:loadbalancer 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

背景

經過前面幾篇的理解,我們大致梳理清楚了FeignClient的創建、Feign調用的大體流程,本篇會深入Feign調用中涉及的另一個重要組件:loadbalancer,了解loadbalancer在feign調用中的職責,再追溯其是如何創建的。

在講之前,我先提個重點,本文章的前期是引用了nacos依賴且開啟了如下選項,啟用了nacos的Loadbalancer:

spring.cloud.loadbalancer.nacos.enabled=true

nacos的Loadbalancer是支持了基于nacos實例中的元數據進行服務實例篩選,比如權重等元數據。

不開這個選項,則是用默認的Loadbalancer,不知道支不支持基于nacos實例中的元數據進行服務實例篩選(沒測試)。

我們這邊是打開了這個選項,所以本文就基于打開的情況來講。

feign調用流程

大體流程

接上一篇文章,feign調用的核心代碼如下:

1處主要是封裝請求;

2處主要是依靠loadbalancer獲取最終要調用的實例。

但是在1和2之間,有一段代碼是,獲取LoadBalancerLifecycle類型的bean列表,大家看到什么lifecycle之類的名字,大概能知道,這些類是一些listener類,一般包含了幾個生命周期相關的方法,比如這里就是:

void onStart(Request<RC> request);

void onStartRequest(Request<RC> request, Response<T> lbResponse);

void onComplete(CompletionContext<RES, T, RC> completionContext);

這幾個方法分別就是在loadbalancer的不同階段進行調用。

比如,我舉個例子,我之前發現feign的日志里沒打印最終調用的實例的ip、端口,導致查日志不方便,所以我就定義了一個自定義的LoadBalancerLifecycle類,將最終選擇的實例的ip端口打印出來。

我們看下,這里是如何獲取LoadBalancerLifecycle對象的?

loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class)

工廠用途

loadBalancerClientFactory這個字段,類型為LoadBalancerClientFactory,其定義:

public class LoadBalancerClientFactory extends NamedContextFactory<LoadBalancerClientSpecification>

再看其注釋:

A factory that creates client, load balancer and client configuration instances. It creates a Spring ApplicationContext per client name, and extracts the beans that it needs from there.

這里就直說了,這是個工廠,它會給每個client創建一個spring容器。這里的client是啥呢,其實是org.springframework.cloud.client.loadbalancer.LoadBalancerClient類型的對象,它是在spring-cloud-commons中定義的接口:

工廠自身的創建

工廠本身是自動裝配的:

看上圖,需要一個構造函數參數,這個就是一些配置:

調用的構造函數邏輯如下:

public class LoadBalancerClientFactory extends NamedContextFactory<LoadBalancerClientSpecification>

public static final String NAMESPACE = "loadbalancer";    
public static final String PROPERTY_NAME = NAMESPACE + ".client.name";

public LoadBalancerClientFactory(LoadBalancerClientsProperties properties) {
    super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME);
    this.properties = properties;
}

這里調用了父類構造函數,把幾個值存到父類中:

private final String propertySourceName;
private final String propertyName;
private Class<?> defaultConfigType;

public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName, String propertyName) {
    this.defaultConfigType = defaultConfigType;
    this.propertySourceName = propertySourceName;
    this.propertyName = propertyName;
}

完成構造后,我們發現,還調用了:

clientFactory.setConfigurations(this.configurations.getIfAvailable(Collections::emptyList));

這里的configurations類型是:

private final ObjectProvider<List<LoadBalancerClientSpecification>> configurations;

這個字段本身是通過構造函數方式注入的,來源呢,就是spring 容器。

我們有必要探究下,這個LoadBalancerClientSpecification類型的bean,是怎么進入spring 容器的?

其實,這個類也是代表了一份LoadbalancerClient的配置,之前feignClient也是一樣的:

public class LoadBalancerClientSpecification implements NamedContextFactory.Specification {

	private String name;

	private Class<?>[] configuration;
}

這種類型的bean,其實是通過LoadBalancerClient注解和LoadBalancerClients注解進入容器的,當你使用這兩個注解時,其實是支持配置一個class:

然后,它們兩注解都import了一個LoadBalancerClientConfigurationRegistrar類:

這個會負責將對應的配置class,注冊到容器中:

注冊時,name會有所區別,如果是LoadBalancerClients注解引入的,會加個default前綴。

在默認情況下(引入了nacos-discovery、spring-cloud-loadbalancer的情況下),就會在代碼中如下三處有@LoadBalancerClients注解:

所以,我們工廠創建時debug,可以看到如下場景:

從工廠獲取LoadBalancerLifecycle

上面講完了工廠的創建,這里回到工廠的使用。我們之前看到,會獲取LoadBalancerLifecycle這種bean:

loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),

但奇怪的是,獲取bean不應該先用loadBalancerClientFactory創建的給各個loadBalancerClient的spring容器;再從容器獲取bean嗎?

這里是簡化了,直接讓工廠負責全部事務,我要bean的時候,只找工廠要,工廠內部自己再去創建spring容器那些。

所以我們看到,工廠是實現了接口:

public class LoadBalancerClientFactory extends NamedContextFactory<LoadBalancerClientSpecification>
		implements ReactiveLoadBalancer.Factory<ServiceInstance>    

這個接口就有如下方法,這是個泛型方法:

Allows accessing beans registered within client-specific LoadBalancer contexts.
    
<X> Map<String, X> getInstances(String name, Class<X> type);

下面就看看方法如何實現的:

這里就是分了兩步,先獲取容器,再從容器獲取bean。

創建容器

這個獲取容器是先從緩存map獲取,沒有則創建。

我們這里自然是沒有的,進入createContext:

這里首先是創建了一個spring上下文,里面是有一個bean容器的,容器里要放什么bean呢,首先就是上圖中的configurations中那些LoadBalancerClient注解里指定的配置類,再然后,就是LoadBalancerClients注解里指定的那些默認的配置類,我們這里有3處LoadBalancerClients注解,但是只有nacos那一個,指定了配置類:

@LoadBalancerClients(defaultConfiguration = NacosLoadBalancerClientConfiguration.class)
public class LoadBalancerNacosAutoConfiguration {

所以,這里會把NacosLoadBalancerClientConfiguration這個配置類注冊到容器。

接下來,是如下這行:

context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);

這里的defaultConfigType是啥呢,其實就是創建工廠時,指定的LoadBalancerClientConfiguration:

到這里為止,基本spring容器該手工放入的bean就這些了。但這個容器內到時候只會有這些bean嗎,不是的。

因為我們這里放進去的幾個bean,內部又定義了更多的bean。

nacosLoadBalancerClientConfiguration
loadBalancerClientConfiguration    

nacosLoadBalancerClientConfiguration

首先是自動裝配一個NacosLoadBalancer(在缺少這種ReactorLoadBalancer bean的情況下)

再下來,會自動裝配ServiceInstanceListSupplier bean:

loadBalancerClientConfiguration

這邊注意,也是在沒注冊這個bean的時候,自動裝配ReactorLoadBalancer,這個其實會和上面的nacos的產生競爭,最終到底是哪個上崗呢,只能看順序了:

和nacos一樣,自動裝配ServiceInstanceListSupplier:

競爭關系誰勝出

我們上面提到,nacos的配置類和spring-cloud-loadbalancer的配置類,是全面競爭的,最終的話,是誰勝出呢?

我們看看容器完成bean創建后的情況:

可以發現,是nacos的配置贏了。

具體為什么贏,這個暫時不細說,基本就是bean的order那些事情。反正現在nacos贏了,看起來也沒啥問題,我們就繼續往后走,目前是完成了bean容器的創建。

獲取LoadBalancerLifecycle類型bean

我這個項目,并沒定義這種bean,所以實際是取不到的,注意的是,在LoadbalancerClient對應的容器取不到,還是會去父容器取的。

我們在父容器也沒定義,所以最終是取不到。

根據服務名獲取最終實例

loadBalancerClient

目前準備分析如下代碼:

先看下這個字段來自于哪里:

可以看出,來自于spring容器注入。

所以,這里可以看出,loadBalancerClient類型為BlockingLoadBalancerClient。

loadBalancerClient.choose

進入該方法:

最終就是從容器獲取,取到的就是nacos自動裝配的NacosLoadBalancer:

loadBalancer.choose

nacos這里的實現用的反應式編程,不怎么了解這塊,反正最終是調用getInstanceResponse方法,且會把從nacos獲取到的服務列表傳遞進來:

可以看到,這里傳入的就是實際的服務實例,還包含了nacos相關的元數據,如cluster、weight、是否臨時、是否健康等。

后續的邏輯就根據實例的各種屬性進行篩選,如meta.nacos.cluster、ipv4/ipv6、

根據權重進行選擇:

根據實例進行feign調用

我們跟進去后,發現主要就是feignClient.execute進行調用,在前后則是調用生命周期的相關方法:

我們看到,這個client就是默認的FeignClient,比較原始,直接就是用原生的HttpURLConnection;我們之前文章提到,也是可以使用httpclient、okhttp那些feign.Client的實現,只要引入對應依賴即可。

另外,這個也是沒有連接池的,每次都是打開新連接;這里也用了外部options參數中的超時時間。

后面的響應處理就略過不講了。

總結

我們總算是把大體流程都講完了,下一篇講講我遇到的問題。

總結

以上是生活随笔為你收集整理的Feign源码解析5:loadbalancer的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。