Spring-Cloud-Loadblancer詳細分析_3

前兩篇文章介紹了加載過程,本文從Feign的入口開始分析執行過程,還是從FeignBlockingLoadBalancerClient.execute來入手

public class FeignBlockingLoadBalancerClient implements Client {private static final Log LOG = LogFactory.getLog(FeignBlockingLoadBalancerClient.class);private final Client delegate;private final LoadBalancerClient loadBalancerClient;private final LoadBalancerClientFactory loadBalancerClientFactory;@Overridepublic Response execute(Request request, Request.Options options) throws IOException {//請求路徑final URI originalUri = URI.create(request.url());//獲取到要調用的服務idString serviceId = originalUri.getHost();DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest<>(new RequestDataContext(buildRequestData(request), hint));//在這步創建了每個服務的子容器		Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator.getSupportedLifecycleProcessors(//在這步創建了每個服務的子容器loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),RequestDataContext.class, ResponseData.class, ServiceInstance.class);supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));		//執行loadBalancer的負載均衡策略,返回將過濾后的服務,非常重要ServiceInstance instance = loadBalancerClient.choose(serviceId, lbRequest);org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(instance);//省略...//將ServiceInstance進行解析后,轉換為真正的http方式進行遠程調用服務String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri).toString();Request newRequest = buildRequest(request, reconstructedUrl);LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(serviceId);return executeWithLoadBalancerLifecycleProcessing(delegate, options, newRequest, lbRequest, lbResponse,supportedLifecycleProcessors, loadBalancerProperties.isUseRawStatusCodeInResponseData());}protected Request buildRequest(Request request, String reconstructedUrl) {return Request.create(request.httpMethod(), reconstructedUrl, request.headers(), request.body(),request.charset(), request.requestTemplate());}private String getHint(String serviceId) {LoadBalancerProperties properties = loadBalancerClientFactory.getProperties(serviceId);String defaultHint = properties.getHint().getOrDefault("default", "default");String hintPropertyValue = properties.getHint().get(serviceId);return hintPropertyValue != null ? hintPropertyValue : defaultHint;}}

loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class)

在這步是創建了每個服務的負載均衡子容器,loadBalancerClientFactory的生成過程在上篇文章已經分析了,這里getInstances實際調用的是父類NamedContextFactory

NamedContextFactory.getInstances

public <T> Map<String, T> getInstances(String name, Class<T> type) {AnnotationConfigApplicationContext context = getContext(name);return BeanFactoryUtils.beansOfTypeIncludingAncestors(context, type);
}

NamedContextFactory.getContext

protected AnnotationConfigApplicationContext getContext(String name) {if (!this.contexts.containsKey(name)) {synchronized (this.contexts) {if (!this.contexts.containsKey(name)) {//創建子容器,name為服務名 value為容器this.contexts.put(name, createContext(name));}}}return this.contexts.get(name);
}

NamedContextFactory.createContext

protected AnnotationConfigApplicationContext createContext(String name) {AnnotationConfigApplicationContext context;//創建容器if (this.parent != null) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();if (parent instanceof ConfigurableApplicationContext) {beanFactory.setBeanClassLoader(((ConfigurableApplicationContext) parent).getBeanFactory().getBeanClassLoader());}else {beanFactory.setBeanClassLoader(parent.getClassLoader());}context = new AnnotationConfigApplicationContext(beanFactory);context.setClassLoader(this.parent.getClassLoader());}else {context = new AnnotationConfigApplicationContext();}//configurations就是LoadBalancerClientSpecification類型的LoadBalancerAutoConfiguration和BlockingLoadBalancerClientAutoConfiguration//這里是將@LoadBalancerClient對應的負載均衡配置注冊到對應的容器中//由以上可知通過此步我們可以使用@LoadBalancerClient自定義負載均衡策略//如果不自定義的話,這里為falseif (this.configurations.containsKey(name)) {for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {context.register(configuration);}}//這時的configurations中會有LoadBalancerClientSpecification類型的LoadBalancerAutoConfiguration和BlockingLoadBalancerClientAutoConfigurationfor (Map.Entry<String, C> entry : this.configurations.entrySet()) {if (entry.getKey().startsWith("default.")) {//不會進去for循環體,因為LoadBalancerAutoConfiguration和BlockingLoadBalancerClientAutoConfiguration//包裝成LoadBalancerClientSpecification后entry.getValue().getConfiguration()沒有值for (Class<?> configuration : entry.getValue().getConfiguration()) {context.register(configuration);}}}//defaultConfigType就是LoadBalancerClientConfiguration,在子類LoadBalancerClientFactory的構造方法傳入//在剛才分析LoadBalancerClientFactory的時候介紹過context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,Collections.<String, Object>singletonMap(this.propertyName, name)));//將父容器添加進去if (this.parent != null) {// Uses Environment from parent as well as beanscontext.setParent(this.parent);}context.setDisplayName(generateDisplayName(name));context.refresh();return context;
}

重點:

可以看到當每個服務創建子容器后,LoadBalancerAutoConfigurationBlockingLoadBalancerClientAutoConfigurationLoadBalancerClientConfiguration這三個非常重要的裝配配置類都注冊到了每個子容器中

服務的子容器創建完成后,下面就開始執行負載均衡的流程了

BlockingLoadBalancerClient.choose

public <T> ServiceInstance choose(String serviceId, Request<T> request) {//獲取負載均衡器ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId);if (loadBalancer == null) {return null;}Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block();if (loadBalancerResponse == null) {return null;}return loadBalancerResponse.getServer();
}

loadBalancerClientFactory.getInstance(serviceId)

public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {return getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class);
}

這時的getInstance調用的是父類NamedContextFactory.getInstance

NamedContextFactory.getInstance

public <T> T getInstance(String name, Class<T> type) {AnnotationConfigApplicationContext context = getContext(name);try {return context.getBean(type);}catch (NoSuchBeanDefinitionException e) {// ignore}return null;
}
protected AnnotationConfigApplicationContext getContext(String name) {if (!this.contexts.containsKey(name)) {synchronized (this.contexts) {if (!this.contexts.containsKey(name)) {this.contexts.put(name, createContext(name));}}}return this.contexts.get(name);
}

剛才介紹了,在BlockingLoadBalancerClient.choose執行之前,子容器已經創建完畢,這里就執行返回

容器返回后,獲取ReactorServiceInstanceLoadBalancer類型的對象,此類型的對象就是在LoadBalancerClientConfiguration創建的,我們再看一眼

@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465;@Bean@ConditionalOnMissingBeanpublic ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}//省略
}	

RoundRobinLoadBalancer結構圖
在這里插入圖片描述
所以loadBalancerClientFactory.getInstance(serviceId)返回的就是RoundRobinLoadBalancer,然后就會調用此策略進行執行

RoundRobinLoadBalancer.choose

public Mono<Response<ServiceInstance>> choose(Request request) {ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get(request).next().map(serviceInstances -> processInstanceResponse(supplier, serviceInstances));
}
  • serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new)就是獲取注冊中心的服務列表了
  • processInstanceResponse執行具體的負載均衡策略

serviceInstanceListSupplierProvider

public T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException {return delegate().getIfAvailable(defaultSupplier);
}default T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException {T dependency = getIfAvailable();//dependency 的類型是CachingServiceInstanceListSupplierreturn (dependency != null ? dependency : defaultSupplier.get());
}

RoundRobinLoadBalancer.processInstanceResponse

private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,List<ServiceInstance> serviceInstances) {//serviceInstances就是獲取的服務列表了	//getInstanceResponse(serviceInstances)就是真正的負債均衡策略了Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {//((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());}return serviceInstanceResponse;
}

RoundRobinLoadBalancer.getInstanceResponse

private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " + serviceId);}return new EmptyResponse();}// Do not move position when there is only 1 instance, especially some suppliers// have already filtered instancesif (instances.size() == 1) {return new DefaultResponse(instances.get(0));}// Ignore the sign bit, this allows pos to loop sequentially from 0 to// Integer.MAX_VALUEint pos = this.position.incrementAndGet() & Integer.MAX_VALUE;ServiceInstance instance = instances.get(pos % instances.size());return new DefaultResponse(instance);
}

可以看到是輪訓的策略

那么服務列表是怎么獲取的呢,在下篇文章中我們回詳細的分析

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/39592.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/39592.shtml
英文地址,請注明出處:http://en.pswp.cn/news/39592.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Vue3實現圖片懶加載及自定義懶加載指令

Vue3實現圖片懶加載及自定義懶加載指令 前言1.使用vue3-lazyload插件2.自定義v-lazy懶加載指令2.1 使用VueUse2.2 使用IntersectionObserver 前言 圖片懶加載是一種常見性能優化的方式&#xff0c;它只去加載可視區域圖片&#xff0c;而不是在網頁加載完畢后就立即加載所有圖片…

clickhouse入門

clickhouse 1 課程介紹 和hadoop無關&#xff0c;俄羅斯&#xff0c;速度快3 介紹&特點 1 列式存儲 在線分析處理。 使用sql進行查詢。列式存儲更適合查詢分析的場景。新增時候有一個尋址的過程。更容易進行壓縮行式存儲。增刪改查都需要的時候。2 DBMS功能 包括ddl,d…

集成DTM實現跨語言分布式事務V1.0

集成DTM實現跨語言分布式事務V1.0 簡介 DTM是一款開源的分布式事務管理器&#xff0c;解決跨數據庫、跨服務、跨語言棧更新數據的一致性問題。 通俗一點說&#xff0c;DTM提供跨服務事務能力&#xff0c;一組服務要么全部成功&#xff0c;要么全部回滾&#xff0c;避免只更新…

MIMO-NOMA系統MATLAB仿真實現

非正交多址接入&#xff08;NOMA&#xff09;技術可以打破傳統的正交多址一個基本資源塊由單用戶獨占的限制&#xff0c;通過在時域和頻域的基礎上開辟新的功率域維度&#xff0c;在相同的時頻資源上通過功率復用技術允許同一個時頻資源塊由多個用戶共享&#xff0c;有效提升了…

ViewPager2與TabLayout的簡單使用

ViewPager2與TabLayout的簡單使用 MainActivity.java public class MainActivity extends AppCompatActivity {private ViewPager2 mViewPager;private TabLayout mTabLayout;private int[] icons new int[]{R.drawable.icon1, R.drawable.icon2, R.drawable.icon3, R.drawa…

如何進行無線網絡滲透測試?

今天我們將繼續深入探討Kali Linux的應用&#xff0c;這次我們將重點介紹如何使用Kali Linux進行無線網絡滲透測試。無線網絡滲透測試是評估無線網絡安全性的重要步驟&#xff0c;而Kali Linux作為一款專業的滲透測試發行版&#xff0c;提供了豐富的工具來幫助你進行這項任務。…

c++中const修飾成員函數的問題

問題引入&#xff1a; 看下面這一段代碼&#xff1a; class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;}void Print(){cout << "Print()" << endl;cout << "year:" << _year <&…

面試熱題(全排列)

給定一個不含重復數字的整數數組 nums &#xff0c;返回其 所有可能的全排列 。可以 按任意順序 返回答案。 輸入&#xff1a;nums [1,2,3] 輸出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 先在這里說明一下排列和組合的區別? 組合&#xff1a;是指從一…

前端三劍客

三劍客 萬維網聯盟&#xff08; World Wide Web Consortium &#xff09;&#xff0c;創建于1994年10月&#xff0c;主要工作是對 web 進行標準化。 ? 該組織定義了網頁的開發需要如下3門技術&#xff1a; ? - HTML:定義網頁的結構 - CSS: 定義網頁的表現&#xff0c;樣式 -…

開源數據庫Mysql_DBA運維實戰 (名詞解釋)

SQL&#xff08;Structured Query Language 即結構化查詢語言&#xff09; SQL語言主要用于存取數據、查詢數據、更新數據和管理關系數據庫系統&#xff0c;SQL語言由IBM開發。 SQL語言分類&#xff1a; DDL語句 數據庫定義語言&#xff1a;數據庫、表、視圖、索引、存儲過程…

Steam 靈感的游戲卡懸停效果

先看效果&#xff1a; 再看代碼&#xff08;查看更多&#xff09;&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Steam 靈感的游戲卡懸停效果</title><style>* {margin: …

構建高效外賣系統平臺:從需求到實現

隨著科技的不斷進步和人們生活節奏的加快&#xff0c;外賣成為了越來越多人的飲食選擇。為了滿足這一需求&#xff0c;開發一套高效的外賣系統平臺變得尤為重要。本文將從需求分析開始&#xff0c;逐步引導您了解如何開發一套完整的外賣系統平臺。 第一步&#xff1a;需求分析…

分類預測 | MATLAB實現EVO-CNN多輸入分類預測

分類預測 | MATLAB實現EVO-CNN多輸入分類預測 目錄 分類預測 | MATLAB實現EVO-CNN多輸入分類預測預測效果基本介紹程序設計參考資料 預測效果 基本介紹 1.MATLAB實現EVO-CNN多輸入分類預測 2.代碼說明&#xff1a;量谷優化卷積神經網絡的數據分類預測&#xff1a;要求于Matlab …

【hadoop】windows上hadoop環境的搭建步驟

文章目錄 前言基礎環境下載hadoop安裝包下載hadoop在windows中的依賴配置環境變量 Hadoop hdfs搭建創建hadfs數據目錄修改JAVA依賴修改配置文件初始化hdfs namenode啟動hdfs 前言 在大數據開發領域中&#xff0c;不得不說說傳統經典的hadoop基礎計算框架。一般我們都會將hadoo…

計算機視覺目標檢測性能指標

目錄 精確率&#xff08;Precision&#xff09;和召回率&#xff08;Recall&#xff09; F1分數&#xff08;F1 Score&#xff09; IoU&#xff08;Intersection over Union&#xff09; P-R曲線&#xff08;Precision-Recall Curve&#xff09;和 AP mAP&#xff08;mean…

Leetcode-每日一題【劍指 Offer 30. 包含min函數的棧】

題目 定義棧的數據結構&#xff0c;請在該類型中實現一個能夠得到棧的最小元素的 min 函數在該棧中&#xff0c;調用 min、push 及 pop 的時間復雜度都是 O(1)。 示例: MinStack minStack new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack…

【mysql】事務的四種特性的理解

&#x1f307;個人主頁&#xff1a;平凡的小蘇 &#x1f4da;學習格言&#xff1a;命運給你一個低的起點&#xff0c;是想看你精彩的翻盤&#xff0c;而不是讓你自甘墮落&#xff0c;腳下的路雖然難走&#xff0c;但我還能走&#xff0c;比起向陽而生&#xff0c;我更想嘗試逆風…

TOMCAT基礎

tomcat是一個基于Java開發的&#xff0c;開放源代碼的web應用服務器。它可以解析html頁面中的java代碼&#xff0c;執行動態請求&#xff0c;實現動態頁面。核心功能是將收到的http請求處理并轉發給適當的servlet來處理&#xff0c;然后將響應返回給客戶端。 優點 1&#xff0c…

Django實現音樂網站 ⑼

使用Python Django框架制作一個音樂網站&#xff0c; 本篇主要是后臺對專輯、首頁輪播圖原有功能的基礎上進行部分功能實現和顯示優化。 目錄 專輯功能優化 新增編輯 專輯語種改為下拉選項 添加單曲優化顯示 新增單曲多選 更新歌手專輯數、專輯單曲數 獲取歌手專輯數 保…

【并發編程】自研數據同步工具的優化:創建線程池多線程異步去分頁調用其他服務接口獲取海量數據

文章目錄 場景&#xff1a;解決方案 場景&#xff1a; 前段時間在做一個數據同步工具&#xff0c;其中一個服務的任務是調用A服務的接口&#xff0c;將數據庫中指定數據請求過來&#xff0c;交給kafka去判斷哪些數據是需要新增&#xff0c;哪些數據是需要修改的。 剛開始的設…