我們在spring-cloud中要使用ribbon做負載均衡的時候一般會添加如下包:

并在@Configuration的javaconfig配置中配置如下restTemplate,加上注解@LoadBalanced則實現(xiàn)了前端負載均衡。

下面看看Ribbon的原理:
核心為org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient類:

LoadBalancerClient的實現(xiàn)類為RibbonLoadBalancerClient,這個類是非常重要的一個類,最終的負載均衡的請求處理,由它來執(zhí)行。
RibbonLoadBalancerClient的核心方法如下:


可知,LoadBalancerClient的選擇服務(wù)器實例的方法最終是由ILoadBalancer的chooseServer來實現(xiàn)。
ILoadBalancer的實現(xiàn)類為com.netflix.loadbalancer.DynamicServerListLoadBalancer:

其中其核心的屬性為IRule和Iping
IRule有很多默認的實現(xiàn)類,這些實現(xiàn)類根據(jù)不同的算法和邏輯來處理負載均衡。Ribbon實現(xiàn)的IRule有一下。在大多數(shù)情況下,這些默認的實現(xiàn)類是可以滿足需求的,如果有特性的需求,可以自己實現(xiàn)。
BestAvailableRule 選擇最小請求數(shù)
ClientConfigEnabledRoundRobinRule 輪詢
RandomRule 隨機選擇一個server
RoundRobinRule 輪詢選擇server
RetryRule 根據(jù)輪詢的方式重試
WeightedResponseTimeRule 根據(jù)響應(yīng)時間去分配一個weight ,weight越低,被選擇的可能性就越低
ZoneAvoidanceRule 根據(jù)server的zone區(qū)域和可用性來輪詢選擇。

IPing是用來想server發(fā)生”ping”,來判斷該server是否有響應(yīng),從而判斷該server是否可用。

PingUrl 真實的去ping 某個url,判斷其是否alive
PingConstant 固定返回某服務(wù)是否可用,默認返回true,即可用
NoOpPing 不去ping,直接返回true,即可用。
DummyPing 直接返回true,并實現(xiàn)了initWithNiwsConfig方法。
NIWSDiscoveryPing,根據(jù)DiscoveryEnabledServer的InstanceInfo的InstanceStatus去判斷,如果為InstanceStatus.UP,則為可用,否則不可用。
在DynamicServerListLoadBalancer的構(gòu)造函數(shù)中會調(diào)用initWithNiwsConfig,配置相應(yīng)的IRule和Iping的屬性,并調(diào)用了restOfInit()方法:



serverListImpl的實現(xiàn)類為com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList類,查看getUpdatedListOfServers()方法:

? ??其中eurekaClientProvider的實現(xiàn)類是LegacyEurekaClientProvider,它是一個獲取eurekaClient類,通過加鎖的實例方法去獲取eurekaClient,其代碼如下:

urekaClient的實現(xiàn)類為DiscoveryClient,由此可見,負載均衡器是從EurekaClient獲取服務(wù)信息,并根據(jù)IRule去路由,并且根據(jù)IPing去判斷服務(wù)的可用性。
負載均衡器多久會同步Eureka的注冊列表數(shù)據(jù)呢?
回到最初的DynamicServerListLoadBalancer構(gòu)造器中,實例化的時候調(diào)用了BaseLoadBalancer的構(gòu)造方法開啟了一個PingTask任務(wù),代碼如下:


setupPingTask()的具體代碼邏輯,它開啟了ShutdownEnabledTimer執(zhí)行PingTask任務(wù),在默認情況下pingIntervalSeconds為10,即每10秒鐘,想EurekaClient發(fā)送一次”ping”。




查看Pinger的runPinger()方法,最終根據(jù) pingerStrategy.pingServers(ping, allServers)來獲取服務(wù)的可用性,如果該返回結(jié)果,如之前相同,則不去向EurekaClient獲取注冊列表,如果不同則通知ServerStatusChangeListener或者changeListeners發(fā)生了改變,進行更新或者重新拉取。
由此可見,LoadBalancerClient是在初始化的時候,會向EurekaClient拉取服務(wù)注冊列表,并且向通過10s一次向EurekaClient發(fā)送“ping”,來判斷服務(wù)的可用性,如果服務(wù)的可用性發(fā)生了改變或者服務(wù)數(shù)量和之前的不一致,則更新或者重新拉取。LoadBalancerClient有了這些服務(wù)注冊列表,就可以根據(jù)具體的IRule來進行負載均衡。
RestTempalate與Ribbon結(jié)合

在該類中,首先維護了一個被@LoadBalanced修飾的RestTemplate對象的List,在初始化的過程中,通過調(diào)用customizer.customize(restTemplate)方法來給RestTemplate增加攔截器LoadBalancerInterceptor。

而LoadBalancerInterceptor,用于實時攔截,在LoadBalancerInterceptor這里實現(xiàn)來負載均衡。LoadBalancerInterceptor的攔截方法如下:

查看RibbonLoadBalancerClient的execute方法:


最終由IRule的策略選取實例。

綜上所述,Ribbon的負載均衡,主要通過LoadBalancerClient來實現(xiàn)的,而LoadBalancerClient具體交給了ILoadBalancer來處理,ILoadBalancer通過配置IRule、IPing等信息,并向EurekaClient獲取注冊列表的信息,并默認10秒一次向EurekaClient發(fā)送“ping”,進而檢查是否更新服務(wù)列表,最后,得到注冊列表后,ILoadBalancer根據(jù)IRule的策略進行負載均衡。
而RestTemplate 被@LoadBalanced注解后,能過用負載均衡,主要是維護了一個被@LoadBalance注解的RestTemplate列表,并給列表中的RestTemplate添加攔截器,進而交給負載均衡器去處理。
參考:https://blog.csdn.net/forezp/article/details/74820899