【SpringCloud】Ribbon源碼解析

e246a1dda09849a5a89535a62441565d.png

ribbon是一個負載均衡組件,它可以將請求分散到多個服務提供者實例中,提高系統的性能和可用性。本章分析ribbon是如何實現負載均衡的

1、@LoadBalanced

消費者在引入ribbon組件后,給http客戶端添加@LoadBalanced注解就能啟用負載均衡功能。@LoadBalanced注解比較簡單,本身沒有包含什么業務邏輯,值得一提的是@Qualifier注解。我們知道@Qualifier通常和@Autowired一起使用,用來指明注入bean的名稱,這樣就能在眾多同類型的bean中選擇真正需要的bean,除此之外@Qualifier也可以不指定名稱,只需要在bean的入口和出口都用@Qualifier修飾,就能建立起對應關系

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
@Component
@Qualifier
public class A {}public class B {// 精準注入A@Autowired@Qualifierprivate A a;
}

2、LoadBalancerAutoConfiguration

和負載均衡真正相關內容在LoadBalancerAutoConfiguration內,查看spring-cloud-common包的spring.factories文件,我們知道這是一個自動配置類

# AutoConfiguration
...
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\
...

LoadBalancerAutoConfiguration注入了所有使用@LoadBalanced修飾的restTemplate,為什么加了@LoadBalanced就能引入其他其他被@LoadBalanced修飾的bean,原因就是1中提到的@Qualifier。

LoadBalancerAutoConfiguration內部還有一個loadBalancedRestTemplateInitializerDeprecated方法,這個方法會對restTemplate進行了定制化處理

@LoadBalanced
@Autowired(required = false
)
private List<RestTemplate> restTemplates = Collections.emptyList();@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {return () -> {restTemplateCustomizers.ifAvailable((customizers) -> {Iterator var2 = this.restTemplates.iterator();while(var2.hasNext()) {RestTemplate restTemplate = (RestTemplate)var2.next();Iterator var4 = customizers.iterator();while(var4.hasNext()) {RestTemplateCustomizer customizer = (RestTemplateCustomizer)var4.next();// 定制化處理customizer.customize(restTemplate);}}});};
}

RestTemplateCustomizer接口的實現類也在LoadBalancerAutoConfiguration內,對restTemplate的定制化其實就是給它添加了RetryLoadBalancerInterceptor攔截器

@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(final RetryLoadBalancerInterceptor loadBalancerInterceptor) {return (restTemplate) -> {List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);// 客戶端添加攔截器restTemplate.setInterceptors(list);};
}

3、RetryLoadBalancerInterceptor

查看RetryLoadBalancerInterceptor的攔截方法,核心語句是loadBalancer.choose,也就是說服務實例的選擇功能其實是由LoadBalancerClient接口實現類完成的

private final LoadBalancerClient loadBalancer;public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {...if (serviceInstance == null) {if (LOG.isDebugEnabled()) {LOG.debug("Service instance retrieved from LoadBalancedRetryContext: was null. Reattempting service instance selection");}// 選擇服務實例serviceInstance = this.loadBalancer.choose(serviceName);if (LOG.isDebugEnabled()) {LOG.debug(String.format("Selected service instance: %s", serviceInstance));}}...ClientHttpResponse response = (ClientHttpResponse)this.loadBalancer.execute(serviceName, serviceInstance, this.requestFactory.createRequest(request, body, execution));       ...
}

RibbonLoadBalancerClient是LoadBalancerClient接口實現類之一,查看它的choose方法

// 選擇合適的服務實例
public ServiceInstance choose(String serviceId, Object hint) {Server server = this.getServer(this.getLoadBalancer(serviceId), hint);return server == null ? null : new RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
}// 選擇合適的服務器
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");
}

選擇服務器的任務移交給了ILoadBalancer的實現類,如ZoneAwareLoadBalancer。ZoneAwareLoadBalancer會調用父類BaseLoadBalancer的choose方法,按照指定的策略選取服務,如輪詢、加權等

public Server chooseServer(Object key) {if (this.counter == null) {this.counter = this.createCounter();}this.counter.increment();if (this.rule == null) {return null;} else {try {// 按規則選擇服務return this.rule.choose(key);} catch (Exception var3) {logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", new Object[]{this.name, key, var3});return null;}}
}

再聊聊服務的來源,LoadBalancer是從哪里挑選的服務?

ZoneAwareLoadBalancer在創建時會調用父類DynamicServerListLoadBalancer的構造方法,然后通過updateListOfServers方法獲取服務列表

public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping, ServerList<T> serverList, ServerListFilter<T> filter, ServerListUpdater serverListUpdater) {...this.restOfInit(clientConfig);
}void restOfInit(IClientConfig clientConfig) {...this.updateListOfServers();...
}@VisibleForTesting
public void updateListOfServers() {List<T> servers = new ArrayList();if (this.serverListImpl != null) {// 獲取服務列表servers = this.serverListImpl.getUpdatedListOfServers();LOGGER.debug("List of Servers for {} obtained from Discovery client: {}", this.getIdentifier(), servers);if (this.filter != null) {servers = this.filter.getFilteredListOfServers((List)servers);LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}", this.getIdentifier(), servers);}}this.updateAllServerList((List)servers);
}

看到服務實例自然聯想到eureka,繼續跟蹤getUpdatedListOfServers方法

調用鏈:
-> DynamicServerListLoadBalancer.updateListOfServers
-> this.serverListImpl.getUpdatedListOfServers;
-> DiscoveryEnabledNIWSServerList.obtainServersViaDiscovery
-> eurekaClient.getInstancesByVipAddress
private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {...// eureka客戶端拉取服務List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, this.isSecure, this.targetRegion);Iterator var8 = listOfInstanceInfo.iterator();...
}

方法體中出現eureka客戶端,也就是說ribbon選擇的服務實例其實來自于eurka服務端,是通過eureka客戶端拉取到本地的

3、總結

ribbon組件向restTemplate中添加攔截器,實現負載均衡功能增強

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

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

相關文章

壓縮包怎么解壓,解壓壓縮包不損壞文件

常見格式&#xff1a; ZIP&#xff1a;最常見的壓縮文件格式之一&#xff0c;支持跨平臺。RAR&#xff1a;另一種常見的壓縮文件格式&#xff0c;通常壓縮率比ZIP高&#xff0c;但不如ZIP普及。7Z&#xff1a;來自7-Zip的壓縮格式&#xff0c;支持更高的壓縮率和一些高級特性。…

手機照片數據恢復,2個技巧解決你的疑惑與困擾

手機照片是我們日常生活中記錄美好瞬間的重要工具&#xff0c;然而&#xff0c;當照片存儲量越來越大&#xff0c;以至于手機內存不足時&#xff0c;我們就不得不放棄一部分。照片數據恢復是否還有希望呢&#xff1f;當然啦&#xff01;本文將為你提供2個實用的技巧&#xff0c…

虛擬機配置與windows之間文件夾共享samba服務:

虛擬機配置與windows之間文件夾共享samba服務: #輸入安裝命令&#xff1a; 第一步: 下載samba cd /etc/ sudo apt-get install samba第二步: 配置用戶 sudo smbpasswd -a 虛擬機用戶名第三步: 進入配置文件配置共享文件 sudo vim /etc/samba/smb.conf末尾輸入以下內容: [s…

經典遞歸題 擴充序列 兩種做法

一道經典遞歸題&#xff0c;兩種做法&#xff0c;常規遞歸做法和模擬數學規律解法 3695. 擴充序列 - AcWing題庫 擴充序列 樣例解釋 對于樣例 1&#xff0c;經過 2 次擴充&#xff0c;得到序列 [1,2,1,3,1,2,1]其第 2 個元素為 2。 對于樣例 2&#xff0c;經過 3次擴充&…

針對airtest的poco標簽正則匹配

1.text屬性方式定位 poco(text“中古屋”) 換成正則表達式定位 poco(textMatches“正則表達式”) poco(textMatches".*中古屋") 2.name屬性方式定位 poco(name‘com.addcn.android.house591:id/grid_item_text’) 換成正則表達式定位 poco(nameMatches“正則表…

Linux下如何設置可執行文件和庫文件的環境變量?

在Linux系統中&#xff0c;可執行文件和庫文件的查找路徑是由環境變量控制的&#xff0c;其中最重要的是PATH環境變量用于可執行文件&#xff0c;而動態庫的查找路徑則由LD_LIBRARY_PATH環境變量決定。下面分別介紹這兩個方面&#xff1a; 可執行文件的搜索路徑&#xff08;PA…

對不起,AI大模型不是風口

“我們正處在全新起點&#xff0c;這是一個以大模型為核心的人工智能新時代&#xff0c;大模型改變了人工智能&#xff0c;大模型即將改變世界。”——5月26日&#xff0c;百度創始人、董事長兼CEO李彥宏先生在2023中關村論壇發表了《大模型改變世界》演講。 李彥宏指出&#…

【SpringCloud】Hystrix源碼解析

hystrix是一個微服務容錯組件&#xff0c;提供了資源隔離、服務降級、服務熔斷的功能。這一章重點分析hystrix的實現原理 1、服務降級 CAP原則是分布式系統的一個理論基礎&#xff0c;它的三個關鍵屬性分別是一致性、可用性和容錯性。當服務實例所在服務器承受過大的壓力或者受…

c++【入門】挖胡蘿卜

限制 時間限制 : 1 秒 內存限制 : 128 MB 題目 小兔朱迪挖了x個胡蘿卜&#xff0c;狐貍尼克挖到胡蘿卜數量是小兔挖到的3倍&#xff0c;小羊肖恩挖到胡蘿卜的數量比狐貍尼克少8個&#xff1b; 請你編程計算一下狐貍尼克和小羊肖恩分別挖了幾個胡蘿卜&#xff0c;以及平均每…

前端工程化09-webpack靜態的模塊化打包工具(未完結)

9.1、開發模式的進化歷史 webpacks是一個非常非常的強大的一個工具&#xff0c;相應的這個東西的學習也是有一定的難度的&#xff0c;里邊的東西非常的多&#xff0c;里面涉及到的 概念的話也是非常非常的多的。 這個東西既然非常重要&#xff0c;那么在我們前端到底處于怎樣…

HCIA4.26-5.10

OSPF ——開放式最短路徑優先協議 無類別鏈路狀態IGP動態路由協議 距離矢量協議 運行距離矢量協議的路由器會周期性的泛洪自己的路由表&#xff0c;通過路由之間的交互&#xff0c;每臺路由器都從相鄰的路由器學習到路由條目&#xff0c;隨后加載進自己的路由表中。對于網絡…

GD32 開發筆記

0x01 GPIO時鐘使能的坑 使用GD32的GPIO引腳來控制 74HC595 &#xff0c;發現引腳一直無法控制&#xff0c;始終輸出3.3v&#xff0c;初始化環節應該是出了問題。用通俗的話來說&#xff0c;就是點燈點不亮 排查了MCU、光耦隔離芯片、被強行上拉等問題&#xff0c;最后發現是G…

Python代碼分析和修復工具庫之coala使用詳解

概要 代碼質量在軟件開發中至關重要,保持代碼的可讀性、一致性和易維護性是每個開發者的目標。coala 是一個開源的代碼分析和修復工具,旨在幫助開發者自動化代碼質量檢查,支持多種編程語言,包括 Python、C++、JavaScript 等。通過使用 coala,開發者可以方便地集成代碼檢查…

AI時代的軟件工程:挑戰與改變

人工智能&#xff08;AI&#xff09;正以驚人的速度改變著我們的生活和工作方式。作為與AI關系最為密切的領域之一&#xff0c;軟件工程正經歷著深刻的轉變。 1 軟件工程的演變 軟件工程的起源 軟件工程&#xff08;Software Engineering&#xff09;是關于如何系統化、規范化地…

input調用手機攝像頭實現拍照功能vue

項目需要一個拍照功能&#xff0c;實現功能如下圖所示:若使用瀏覽器則可以直接上傳圖片&#xff0c;若使用手機則調用手機攝像頭拍照。 1.代碼結構 <!--input標簽--> <input ref"photoRef"type"file"accept"image/*"capture"envir…

Leetcode 3202. Find the Maximum Length of Valid Subsequence II

Leetcode 3202. Find the Maximum Length of Valid Subsequence II 1. 解題思路2. 代碼實現 題目鏈接&#xff1a;3202. Find the Maximum Length of Valid Subsequence II 1. 解題思路 這一題的話是上一題3201. Find the Maximum Length of Valid Subsequence I的升級版&am…

基于多源數據的密碼攻防領域知識圖譜構建

源自&#xff1a; 信息安全與通信保密雜志社 作者&#xff1a;曹增輝 , 郭淵博 , 黃慧敏 摘 要 提高網絡空間安全的密碼攻防能力&#xff0c;需要形成可表示、可共享、可分析的領域知識模式和知識庫。利用自頂向下的構建方法&#xff0c;并通過本體構建方法梳理密碼攻防領域…

IPSec:互聯網協議安全機制的深度解析與應用

目錄 一、IPSec概述 二、IPSec的組成 三、IPSec的工作原理 四、IPSec的用途 IPSec&#xff08;Internet Protocol Security&#xff09;作為現代網絡通信中不可或缺的安全基礎設施&#xff0c;旨在為基于IP&#xff08;Internet Protocol&#xff09;的數據傳輸提供端到端的…

MySQL數據庫鎖詳解

MySQL數據庫鎖詳解 在多用戶環境下&#xff0c;數據庫鎖用于保證事務的完整性和數據的一致性。MySQL提供了多種不同類型的鎖&#xff0c;以適應不同的并發需求和性能考慮。本文將詳細介紹MySQL中的鎖機制&#xff0c;包括鎖的類型、鎖定機制的原理以及如何管理鎖。 1. 鎖的類…

【Linux】虛擬機安裝openEuler 24.03 X86_64 教程

目錄 一、概述 1.1 openEuler 覆蓋全場景的創新平臺 1.2 系統框架 1.3 平臺框架 二、安裝詳細步驟 一、概述 1.1 openEuler 覆蓋全場景的創新平臺 openEuler 已支持 x86、Arm、SW64、RISC-V、LoongArch 多處理器架構&#xff0c;逐步擴展 PowerPC 等更多芯片架構支持&…