javascript
SpringCloud LoadBalancerClient 负载均衡原理
????????LoadBalancerClient 是 SpringCloud 提供的一種負載均衡客戶端,Ribbon 負載均衡組件內部也是集成了 LoadBalancerClient 來實現負載均衡。那么 LoadBalancerClient 內部到底是如何做到的呢?我們先大概講一下 LoadBalancerClient 的實現原理,然后再深入源碼中進行解析。
????????LoadBalancerClient 在初始化時會通過 Eureka Client 向 Eureka 服務端獲取所有服務實例的注冊信息并緩存在本地,并且每10秒向 EurekaClient 發送 “ping”,來判斷服務的可用性。如果服務的可用性發生了改變或者服務數量和之前的不一致,則更新或者重新拉取。最后,在得到服務注冊列表信息后,ILoadBalancer 根據 IRule 的策略進行負載均衡(默認策略為輪詢)。
????????當使用 LoadBalancerClient 進行遠程調用的負載均衡時,LoadBalancerClient 先通過目標服務名在本地服務注冊清單中獲取服務提供方的某一個實例,比如訂單服務需要訪問商品服務,商品服務有3個節點,LoadBalancerClient 會通過 choose() 方法獲取到3個節點中的一個服務,拿到服務的信息之后取出服務IP信息,就可以得到完整的想要訪問的IP地址和接口,最后通過 RestTempate 訪問商品服務。
深入解析 LoadBalancerClient 接口源碼:
1、LoadBalancerClient 源碼解析:
????????LoadBalancerClient 是 Spring Cloud 提供的一個非常重要的接口,它繼承自 ServiceInstanceChooser 接口,該接口的實現類是 RibbonLoadBalanceClient,它們之間的關系如下圖所示:
(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); }????????可以發現 LoadBalancerClient 接口繼承了 ServiceInstanceChooser 接口,主要的方法為2個 execute() 方法,均是用來執行請求的。還有個 reconstructURI() 是用來重構URL的。
(2)ServiceInstanceChooser 接口源碼:
繼續查看 LoadBalancerClient 繼承的 ServiceInstanceChooser 接口源碼,具體如下:
public interface ServiceInstanceChooser {ServiceInstance choose(String serviceId); }????????ServiceInstanceChooser 接口中的主要方法為 choose(),該方法用于根據服務的名稱 serviceId 來選擇其中一個服務實例,即根據 serviceId 獲取ServiceInstance。
(3)RibbonLoadBalanceClient 實現類源碼:
????????接下來我們看看 LoadBalancerClient 的實現類 RibbonLoadBalanceClient,它用來執行最終的負載均衡請求。其中,RibbonLoadBalanceClient 的一個 choose() 方法用于選擇具體的服務實例,其內部是通過 getServer() 方法交給 ILoadBalancer 完成的。我們先看下 RibbonLoadBalanceClient 里面幾個重要實現方法的源碼:
① 第一個:choose(),用來選擇具體的服務實例。
@Overridepublic 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));}② 第二個:getServer(),獲取實例。
protected Server getServer(ILoadBalancer loadBalancer) {return getServer(loadBalancer, null);}protected Server getServer(ILoadBalancer loadBalancer, Object hint) {if (loadBalancer == null) {return null;}// 最終通過 loadBalancer 去做服務實例的選擇。// Use 'default' on a null hint, or just pass it on?return loadBalancer.chooseServer(hint != null ? hint : "default");}????????可以看到最終通過 loadBalancer 去做服務實例的選擇。那我們下面就看下 loadBalancer 是怎么怎么實現服務實例的選擇的?
(4)BaseLoadBalancer 源碼:
????????我們從上面能看到 ILoadBalancer 中的 chooseServer 方法里面默認值為 default,進入ILoadBalancer 實現類 BaseLoadBalancer 的 chooseServer() 看下:
?????????我們的 key 的值為“default”,那么這個 key 代表的是什么意思呢?進去 rule 對象里面看下:
????????可以看到這個 rule 是 IRule 接口聲明出來的,且默認定義的實現類是 RoundRobinRule(),也就是輪詢策略。那我們接下來看下 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個方法,choose() 是用來選擇實例的,setLoadBalancer() 和 getLoadBalance() 用來設置和獲取 ILoadBalancer 的。那么接下來 IRule 接口有多少個實現類:
?(1)隨機策略 RandomRule:隨機數選擇服務列表中的服務節點Server,如果當前節點不可用,則進入下一輪隨機策略,直到選到可用服務節點為止
(2)輪詢策略 RoundRobinRule:按照接收的請求順序,逐一分配到不同的后端服務器
(3)重試策略 RetryRule:在選定的負載均衡策略機上重試機制,在一個配置時間段內當選擇Server不成功,則一直嘗試使用 subRule 的方式選擇一個可用的server;
(4)可用過濾策略 PredicateBaseRule:過濾掉連接失敗 和 高并發連接 的服務節點,然后從健康的服務節點中以線性輪詢的方式選出一個節點返回
(5)響應時間權重策略 WeightedRespinseTimeRule:根據服務器的響應時間分配一個權重weight,響應時間越長,weight越小,被選中的可能性越低。主要通過后臺線程定期地從 status 里面讀取平均響應時間,為每個 server 計算一個 weight
(6)并發量最小可用策略 BestAvailableRule:選擇一個并發量最小的服務節點 server。ServerStats 的 activeRequestCount 屬性記錄了 server 的并發量,輪詢所有的server,選擇其中 activeRequestCount 最小的那個server,就是并發量最小的服務節點。該策略的優點是可以充分考慮每臺服務節點的負載,把請求打到負載壓力最小的服務節點上。但是缺點是需要輪詢所有的服務節點,如果集群數量太大,那么就會比較耗時。
(7)區域權重策略 ZoneAvoidanceRule:綜合判斷 server 所在區域的性能 和 server 的可用性,使用 ZoneAvoidancePredicate 和 AvailabilityPredicate 來判斷是否選擇某個server,前一個判斷判定一個zone的運行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate 用于過濾掉連接數過多的Server。
同樣的,如果我們也可以通過實現 IRule 接口來自定義一個負載均衡策略。
2、ILoadBalancer 源碼解析:
????????ILoadBalancer 是一個接口,該接口定義了一系列實現負載均衡的方法,LoadBalancerClient 的實現類 RibbonLoadBalanceClient 也將負載均衡的具體實現交給了 ILoadBalancer 來處理,ILoadBalancer 通過配置 IRule、IPing 等,向 EurekaClient 獲取注冊列表信息,默認每10秒向 EurekaClient 發送一次 “ping”,進而檢查是否需要更新服務的注冊列表信息。最后,在得到服務注冊列表信息后,ILoadBalancer 根據 IRule 的策略進行負載均衡。ILoadBalancer 接口的實現類結果如下圖所示:
????????查看 BaseLoadBalancer 和 DynamicServerListLoadBalancer 源碼,默認情況下實現了以下配置:
(1)IClientConfig clientConfig:用于配置負載均衡客戶端,默認實現類是 DefaultClientConfigImpl。
(2)IRule rule:用于配置負載均衡的策略,默認使用的是 RoundRobinRule 輪詢策略。
(3)IPing ping:用于檢查當前服務是否有響應,從而判斷當前服務是否可用,默認實現類是 DummyPing,該實現類的 isAlive() 方法返回值是 true,默認所有服務實例都是可用的。
(4)ServerList serverList:用于獲取所有 Server 注冊列表信息。通過跟蹤源碼會發現,ServerList 的實現類是 DiscoveryEnabledNIWSServerList,該類定義的 obtainServersViaDiscovery() 方法是根據 eurekaClientProvider.get() 方法獲取 EurekaClient,再根據 EurekaClient 獲取服務注冊列表信息。EurekaClient 的實現類是DiscoveryClient,DiscoveryClient 具有服務注冊、獲取服務注冊列表等功能。
(5)ServerListFilter filter:定義了根據配置過濾或者動態獲取符合條件的服務列表,默認實現類是 ZonePreferenceServerListFilter,該策略能夠優先過濾出與請求調用方處于同區域的服務實例。
總結
以上是生活随笔為你收集整理的SpringCloud LoadBalancerClient 负载均衡原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Nacos注册中心的部署与用法详细介绍
- 下一篇: Nacos配置中心用法详细介绍