SpringCloud?LoadBalancerClient?負(fù)載均衡原理解析
LoadBalancerClient 是 SpringCloud 提供的一種負(fù)載均衡客戶端,Ribbon 負(fù)載均衡組件內(nèi)部也是集成了 LoadBalancerClient 來(lái)實(shí)現(xiàn)負(fù)載均衡。那么 LoadBalancerClient 內(nèi)部到底是如何做到的呢?我們先大概講一下 LoadBalancerClient 的實(shí)現(xiàn)原理,然后再深入源碼中進(jìn)行解析。
LoadBalancerClient 在初始化時(shí)會(huì)通過(guò) Eureka Client 向 Eureka 服務(wù)端獲取所有服務(wù)實(shí)例的注冊(cè)信息并緩存在本地,并且每10秒向 EurekaClient 發(fā)送 “ping”,來(lái)判斷服務(wù)的可用性。如果服務(wù)的可用性發(fā)生了改變或者服務(wù)數(shù)量和之前的不一致,則更新或者重新拉取。最后,在得到服務(wù)注冊(cè)列表信息后,ILoadBalancer 根據(jù) IRule 的策略進(jìn)行負(fù)載均衡(默認(rèn)策略為輪詢)。
當(dāng)使用 LoadBalancerClient 進(jìn)行遠(yuǎn)程調(diào)用的負(fù)載均衡時(shí),LoadBalancerClient 先通過(guò)目標(biāo)服務(wù)名在本地服務(wù)注冊(cè)清單中獲取服務(wù)提供方的某一個(gè)實(shí)例,比如訂單服務(wù)需要訪問商品服務(wù),商品服務(wù)有3個(gè)節(jié)點(diǎn),LoadBalancerClient 會(huì)通過(guò) choose() 方法獲取到3個(gè)節(jié)點(diǎn)中的一個(gè)服務(wù),拿到服務(wù)的信息之后取出服務(wù)IP信息,就可以得到完整的想要訪問的IP地址和接口,最后通過(guò) RestTempate 訪問商品服務(wù)。
深入解析 LoadBalancerClient 接口源碼:
1、LoadBalancerClient 源碼解析:
LoadBalancerClient 是 Spring Cloud 提供的一個(gè)非常重要的接口,它繼承自 ServiceInstanceChooser 接口,該接口的實(shí)現(xiàn)類是 RibbonLoadBalanceClient,它們之間的關(guān)系如下圖所示:
(1)LoadBalancerClient 接口源碼:
首先我們開始追蹤 LoadBalancerClient 源碼:
public interface LoadBalancerClient extends ServiceInstanceChooser { <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException; <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException; URI reconstructURI(ServiceInstance instance, URI original); }
可以發(fā)現(xiàn) LoadBalancerClient 接口繼承了 ServiceInstanceChooser 接口,主要的方法為2個(gè) execute() 方法,均是用來(lái)執(zhí)行請(qǐng)求的。還有個(gè) reconstructURI() 是用來(lái)重構(gòu)URL的。
(2)ServiceInstanceChooser 接口源碼:
繼續(xù)查看 LoadBalancerClient 繼承的 ServiceInstanceChooser 接口源碼,具體如下:
public interface ServiceInstanceChooser { ServiceInstance choose(String serviceId); }
ServiceInstanceChooser 接口中的主要方法為 choose(),該方法用于根據(jù)服務(wù)的名稱 serviceId 來(lái)選擇其中一個(gè)服務(wù)實(shí)例,即根據(jù) serviceId 獲取ServiceInstance。
(3)RibbonLoadBalanceClient 實(shí)現(xiàn)類源碼:
接下來(lái)我們看看 LoadBalancerClient 的實(shí)現(xiàn)類 RibbonLoadBalanceClient,它用來(lái)執(zhí)行最終的負(fù)載均衡請(qǐng)求。其中,RibbonLoadBalanceClient 的一個(gè) choose() 方法用于選擇具體的服務(wù)實(shí)例,其內(nèi)部是通過(guò) getServer() 方法交給 ILoadBalancer 完成的。我們先看下 RibbonLoadBalanceClient 里面幾個(gè)重要實(shí)現(xiàn)方法的源碼:
① 第一個(gè):choose(),用來(lái)選擇具體的服務(wù)實(shí)例。
@Override public ServiceInstance choose(String serviceId) { return choose(serviceId, null); } /** * New: Select a server using a 'key'. * @param serviceId of the service to choose an instance for * @param hint to specify the service instance * @return the selected {@link ServiceInstance} */ public ServiceInstance choose(String serviceId, Object hint) { Server server = getServer(getLoadBalancer(serviceId), hint); if (server == null) { return null; } return new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server)); }
② 第二個(gè):getServer(),獲取實(shí)例。
protected Server getServer(ILoadBalancer loadBalancer) { return getServer(loadBalancer, null); } protected Server getServer(ILoadBalancer loadBalancer, Object hint) { if (loadBalancer == null) { return null; } // 最終通過(guò) loadBalancer 去做服務(wù)實(shí)例的選擇。 // Use 'default' on a null hint, or just pass it on? return loadBalancer.chooseServer(hint != null ? hint : "default"); }
可以看到最終通過(guò) loadBalancer 去做服務(wù)實(shí)例的選擇。那我們下面就看下 loadBalancer 是怎么怎么實(shí)現(xiàn)服務(wù)實(shí)例的選擇的?
(4)BaseLoadBalancer 源碼:
我們從上面能看到 ILoadBalancer 中的 chooseServer 方法里面默認(rèn)值為 default,進(jìn)入ILoadBalancer 實(shí)現(xiàn)類 BaseLoadBalancer 的 chooseServer() 看下:
我們的 key 的值為“default”,那么這個(gè) key 代表的是什么意思呢?進(jìn)去 rule 對(duì)象里面看下:
可以看到這個(gè) rule 是 IRule 接口聲明出來(lái)的,且默認(rèn)定義的實(shí)現(xiàn)類是 RoundRobinRule(),也就是輪詢策略。那我們接下來(lái)看下 IRule 接口:
(5)IRule 接口源碼:
public interface IRule{ /* * choose one alive server from lb.allServers or * lb.upServers according to key * * @return choosen Server object. NULL is returned if none * server is available */ public Server choose(Object key); public void setLoadBalancer(ILoadBalancer lb); public ILoadBalancer getLoadBalancer(); }
我們可以看到 IRule 接口定義了3個(gè)方法,choose() 是用來(lái)選擇實(shí)例的,setLoadBalancer() 和 getLoadBalance() 用來(lái)設(shè)置和獲取 ILoadBalancer 的。那么接下來(lái) IRule 接口有多少個(gè)實(shí)現(xiàn)類:
(1)隨機(jī)策略 RandomRule:隨機(jī)數(shù)選擇服務(wù)列表中的服務(wù)節(jié)點(diǎn)Server,如果當(dāng)前節(jié)點(diǎn)不可用,則進(jìn)入下一輪隨機(jī)策略,直到選到可用服務(wù)節(jié)點(diǎn)為止
(2)輪詢策略 RoundRobinRule:按照接收的請(qǐng)求順序,逐一分配到不同的后端服務(wù)器
(3)重試策略 RetryRule:在選定的負(fù)載均衡策略機(jī)上重試機(jī)制,在一個(gè)配置時(shí)間段內(nèi)當(dāng)選擇Server不成功,則一直嘗試使用 subRule 的方式選擇一個(gè)可用的server;
(4)可用過(guò)濾策略 PredicateBaseRule:過(guò)濾掉連接失敗 和 高并發(fā)連接 的服務(wù)節(jié)點(diǎn),然后從健康的服務(wù)節(jié)點(diǎn)中以線性輪詢的方式選出一個(gè)節(jié)點(diǎn)返回
(5)響應(yīng)時(shí)間權(quán)重策略 WeightedRespinseTimeRule:根據(jù)服務(wù)器的響應(yīng)時(shí)間分配一個(gè)權(quán)重weight,響應(yīng)時(shí)間越長(zhǎng),weight越小,被選中的可能性越低。主要通過(guò)后臺(tái)線程定期地從 status 里面讀取平均響應(yīng)時(shí)間,為每個(gè) server 計(jì)算一個(gè) weight
(6)并發(fā)量最小可用策略 BestAvailableRule:選擇一個(gè)并發(fā)量最小的服務(wù)節(jié)點(diǎn) server。ServerStats 的 activeRequestCount 屬性記錄了 server 的并發(fā)量,輪詢所有的server,選擇其中 activeRequestCount 最小的那個(gè)server,就是并發(fā)量最小的服務(wù)節(jié)點(diǎn)。該策略的優(yōu)點(diǎn)是可以充分考慮每臺(tái)服務(wù)節(jié)點(diǎn)的負(fù)載,把請(qǐng)求打到負(fù)載壓力最小的服務(wù)節(jié)點(diǎn)上。但是缺點(diǎn)是需要輪詢所有的服務(wù)節(jié)點(diǎn),如果集群數(shù)量太大,那么就會(huì)比較耗時(shí)。
(7)區(qū)域權(quán)重策略 ZoneAvoidanceRule:綜合判斷 server 所在區(qū)域的性能 和 server 的可用性,使用 ZoneAvoidancePredicate 和 AvailabilityPredicate 來(lái)判斷是否選擇某個(gè)server,前一個(gè)判斷判定一個(gè)zone的運(yùn)行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate 用于過(guò)濾掉連接數(shù)過(guò)多的Server。
同樣的,如果我們也可以通過(guò)實(shí)現(xiàn) IRule 接口來(lái)自定義一個(gè)負(fù)載均衡策略。
2、ILoadBalancer 源碼解析:
ILoadBalancer 是一個(gè)接口,該接口定義了一系列實(shí)現(xiàn)負(fù)載均衡的方法,LoadBalancerClient 的實(shí)現(xiàn)類 RibbonLoadBalanceClient 也將負(fù)載均衡的具體實(shí)現(xiàn)交給了 ILoadBalancer 來(lái)處理,ILoadBalancer 通過(guò)配置 IRule、IPing 等,向 EurekaClient 獲取注冊(cè)列表信息,默認(rèn)每10秒向 EurekaClient 發(fā)送一次 “ping”,進(jìn)而檢查是否需要更新服務(wù)的注冊(cè)列表信息。最后,在得到服務(wù)注冊(cè)列表信息后,ILoadBalancer 根據(jù) IRule 的策略進(jìn)行負(fù)載均衡。ILoadBalancer 接口的實(shí)現(xiàn)類結(jié)果如下圖所示:
查看 BaseLoadBalancer 和 DynamicServerListLoadBalancer 源碼,默認(rèn)情況下實(shí)現(xiàn)了以下配置:
(1)IClientConfig clientConfig:用于配置負(fù)載均衡客戶端,默認(rèn)實(shí)現(xiàn)類是 DefaultClientConfigImpl。
(2)IRule rule:用于配置負(fù)載均衡的策略,默認(rèn)使用的是 RoundRobinRule 輪詢策略。
(3)IPing ping:用于檢查當(dāng)前服務(wù)是否有響應(yīng),從而判斷當(dāng)前服務(wù)是否可用,默認(rèn)實(shí)現(xiàn)類是 DummyPing,該實(shí)現(xiàn)類的 isAlive() 方法返回值是 true,默認(rèn)所有服務(wù)實(shí)例都是可用的。
(4)ServerList serverList:用于獲取所有 Server 注冊(cè)列表信息。通過(guò)跟蹤源碼會(huì)發(fā)現(xiàn),ServerList 的實(shí)現(xiàn)類是 DiscoveryEnabledNIWSServerList,該類定義的 obtainServersViaDiscovery() 方法是根據(jù) eurekaClientProvider.get() 方法獲取 EurekaClient,再根據(jù) EurekaClient 獲取服務(wù)注冊(cè)列表信息。EurekaClient 的實(shí)現(xiàn)類是DiscoveryClient,DiscoveryClient 具有服務(wù)注冊(cè)、獲取服務(wù)注冊(cè)列表等功能。
(5)ServerListFilter filter:定義了根據(jù)配置過(guò)濾或者動(dòng)態(tài)獲取符合條件的服務(wù)列表,默認(rèn)實(shí)現(xiàn)類是 ZonePreferenceServerListFilter,該策略能夠優(yōu)先過(guò)濾出與請(qǐng)求調(diào)用方處于同區(qū)域的服務(wù)實(shí)例。
到此這篇關(guān)于SpringCloud LoadBalancerClient 負(fù)載均衡原理的文章就介紹到這了,更多相關(guān)SpringCloud LoadBalancerClient 負(fù)載均衡內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一文詳解如何在控制臺(tái)顯示MyBatis的SQL語(yǔ)句
這篇文章主要為大家介紹了如何在控制臺(tái)顯示MyBatis的SQL語(yǔ)句實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06java 打印一字符串,并在main()方法內(nèi)調(diào)用它
編寫一個(gè)方法(名字自定,但要符合Java編碼規(guī)范),方法內(nèi)打印一字符串,并在main()方法內(nèi)調(diào)用它。2017-02-02Java 如何通過(guò)Magic 魔數(shù)獲取文件類型
魔數(shù)有很多種定義,這里我們討論的主要是在編程領(lǐng)域的定義,文件的起始幾個(gè)字節(jié)的內(nèi)容是固定的,本文給大家介紹Java Magic 魔數(shù)獲取文件類型的相關(guān)知識(shí),感興趣的朋友一起看看吧2023-11-11Spring Security LDAP實(shí)現(xiàn)身份驗(yàn)證的項(xiàng)目實(shí)踐
在本文中,我們涵蓋了“使用 Spring Boot 的 Spring Security LDAP 身份驗(yàn)證示例”的所有理論和示例部分,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08你知道在Java中Integer和int的這些區(qū)別嗎?
最近面試,突然被問道,說(shuō)一下Integer和int的區(qū)別.額…可能平時(shí)就知道寫一些業(yè)務(wù)代碼,包括面試的一些Spring源碼等,對(duì)于這種特別基礎(chǔ)的反而忽略了,導(dǎo)致面試的時(shí)候突然被問到反而不知道怎么回答了.哎,還是乖乖再看看底層基礎(chǔ),順帶記錄一下把 ,需要的朋友可以參考下2021-06-06