連載:2-spring-cloud-ribbon
負載均衡Ribbon
【GitHub地址:https://github.com/NullPointer8023/eureka】
前言:筆記承接上篇,上篇主要針對Eureka的Server和Client,如何實現了服務的註冊和發現。同時簡述了Eureka的實現機制,如何實現Eureka的高可用進行了整合。接下來就要實現如何結合Spring Cloud中Ribbon負載均衡元件實現服務的消費的負載均衡的。
一、什麼是RestTemplate
RestTemplate和Ribbon結合作為服務消費者去消費服務,RestTemplate是Spring Resources中一個訪問第三方RESTful API介面的網路請求框架。RestTemplate的設計原則和其他Spring Template(JDBC Template等)類似,都是為執行復雜任務提供了一個具有預設行為的簡單方法。
RestTemplate支援常見的Http協議的請求方法,所以用它來構建RESTful API。RestTemplate是用來消費REST服務的,所以主要方法都是與REST的HTTP協議的一些方法相連,例如HEAD、GET、POST、PUT、DELETE、OPTIONS等方法,這些方法在RestTemplate類對應的方法為headForHeaders()、getForObject()、postForObject()、put()、delete()等。
二、什麼是Ribbon
負載均衡是指將負載均攤到多個執行單元上,常見的負載均衡有兩種。
1、獨立程序單元,通過負載均衡策略,將請求轉發到不同的執行單元上,例如Nginx。
2、將負載均衡邏輯以程式碼的形式封裝到服務消費者的客戶端上,服務消費者客戶端維護了一份服務提供者的資訊,有了資訊列表,通過負載均衡策略將請求分攤給多個服務提供者,從而達到負載均衡的目的。
Ribbon是Netflix公司開源的一個負載均衡元件,它屬於第二種方式。
在Spring cloud構建的微服務系統中,Ribbon作為服務消費者的負載均衡器,有兩種使用方式,一種是和RestTemplate相結合,另一種是和Feign相結合。Feign已經預設繼承了Ribbon。
三、如何將Ribbon和RestTemplate結合實現負載均衡
繼續沿用上篇筆記中的專案,
Eureka Server:三個例項,埠:peer1:8761、peer2:8762、peer3:8763
Eureka Client:兩個例項,埠:eureka-client:8764、eureka-client:8765,注意:兩個服務的yml中的服務名相同
新建一個module,名為eureka-ribbon-client,也作為一個服務註冊到Eureka Server中,
專案結構:
配置如下:
1、pom.xml
新增對ribbon的引用
2、application.yml
3、Application入口類新增@EnableEurekaClient開啟Eureka Client功能
4、寫一個Restful API介面,內部去呼叫client中的api介面“/hi”,這個過程叫做服務消費。
eureka-client的兩個例項,埠為eureka-client:8764、eureka-client:8765。在呼叫client的介面時,希望輪流訪問這兩個client的例項,這時需要講RestTemplate和Ribbon相結合來進行負載均衡。
這時,需要再程式的IoC容器中注入一個restTemplate的bean,並在這個bean上加上@LoadBalanced註解,此時,RestTemplate就結合了Ribbon開啟了負載均衡功能。
5、編寫service類,呼叫client中的“/hi”介面,此處通過client的服務名即可訪問
6、編寫controller,通過get方法呼叫service的方法
程式碼準備完畢,啟動3個Eureka服務註冊和發現叢集,啟動2個eureka-client例項,啟動ribbon,一共6個服務
示例是在本機一個springboot下啟動多個module實現,所以選擇使用相同的服務名,不同的埠來實現啟動多個示例,達到叢集的效果。
真正專案實戰過程中,需要將服務部署在不同的docker中,服務名相同,埠相同,來實現叢集。
Eureka-Server-peer1:
Eureka-Server-peer2:
Eureka-Server-peer3:
Eureka-Client-ribbon:通過ribbon“localhost:8766” 訪問client服務,多次訪問可以發現負載均衡器起了作用,通過相同的地址訪問client提供的API介面,負載均衡器會輪流地請求eureka-client兩個例項中
此時,完成了restTemplate結合Ribbon實現負載均衡的目的。
gitHub提供了Ribbon脫離RestTemplate使用負載均衡的方式,通過LoadBalancerClient的choose方法也可實現。
【GitHub地址:https://github.com/NullPointer8023/eureka】
四、原始碼分析
1、首先跟蹤【private LoadBalancerClient loadBalancerClient;】瞭解LoadBalancerClient
LoadBanlancerClient是一個介面類,它繼承了ServiceInstanceChooser,LoadBanlancerClient的實現類為RibbonloadBalanceClient。
- LoadBalancerClient是一個負載均衡的客戶端,三種方法,2個execute()用來執行請求,reconstructURI()用於重構Url。
- ServiceInstanceChooser介面choose()方法,根據serviceId獲取ServiceInstance,通過服務名來獲取服務例項。
- RibbonloadBalanceClient是一個非常重要的類,最終的負載均衡的請求處理由它來執行。
- choose()方法用於選擇具體服務例項,getServer()方法用於獲取例項,最終交給ILoadBalancer類去選擇服務例項。
- ILoadBalancer介面,子類BaseLoadBalancer。BaseLoadBalancer的實現類為DynamicServerListLoadBalancer。
- DynamicServerListLoadBalancer類需要配置IClientConfig、IRule、IPing、ServerList、ServerListFilter、ILoadBalancer。
- IClientConfig 用於配置負載均衡的客戶端
- IRule是用於配置負載均衡的策略,有很多預設的實現類,根據不同演算法和邏輯來處理負載均衡的策略。
- IPing用於向Server傳送“ping”,來判斷該server是否有響應,從而判斷該server是否可用。
DynamicServerListLoadBalancer建構函式有一個initWithNiwsConfig()方法,經過一系列初始化,最終呼叫restOfInit()方法,內部有一個方法updateListOfServers()獲取所有ServerList。ServerList是一個介面,其中有一個實現類DiscoveryEnabledNIWSServerList,繼續追蹤類的方法getInitialListOfServers()會發現eurekaClientProvider.get()來獲取EurekaClient。
由此可見,Ribbon的負載均衡是由Eureka Client獲取註冊的服務列表資訊,並根據IRule負載均衡策略去路由,根據IPing判斷服務可用性。
2、負載均衡器每隔多久向Eureka獲取服務列表資訊?
BaseLoadBalancer的構造方法中有一個開啟了PingTask任務。setupPingTask有一個變數pingIntervalSeconds【protected int pingIntervalSeconds = 10;】每十秒向Eureka Client傳送一次心跳“ping”。
檢視PingTask原始碼new Pinger物件,呼叫了runPinger()方法。進一步進入原始碼發現【pingerStrategy.pingServers(ping, allServers);】獲取服務的可用性,如果該返回結果與之前相同,則不向EurekaClient獲取註冊列表,如果不同,則通知ServerStatusChangeListener服務註冊列表資訊發生改變重新拉取。
由此可見,LoadBalancerClient是在初始化時向Eureka獲取服務註冊列表資訊,並且每10秒向EurekaClient傳送“ping”,來判斷服務的可用性。如果服務的可用性發生了改變或者服務數量以前不一致,則更新或者重新拉取。LoadBalancerClient有了新的服務註冊列表,根據IRule的策略來進行負載均衡。
3、為什麼restTemplate類上加了一個@LoadBalance註解就可以使用Ribbon的負載均衡呢?
在LoadBalancerAutoConfiguration類中,首先維護了一個被@LoadBalanced修飾的RestTemplate物件的list。在初始化的過程中,通過呼叫customizer.customize(restTemplate);方法來給RestTemplate增加攔截器LoadBalancerInterceptor。攔截器用於實時攔截,在LoadBalancerInterceptor中實現了負載均衡的方法。
總結:Ribbon的負載均衡主要是通過LoadBalancerClient來實現的,而LoadBalancerClient具體交給了ILoadBalancer來處理,ILoadBalancer通過配置IRule、IPing等向EurekaClient獲取註冊列表資訊,預設每10秒傳送一次“ping”,進而進行檢查服務可用性,是否需要更新服務列表資訊。最後在得到服務註冊列表後,根據IRule的策略進行負載均衡。
而RestTemplate加上@LoadBalancer註解後,遠端排程時能夠負載均衡,主要是維護了一個被@LoadBalancer註解的RestTemplate列表,並給該列表中的RestTemplate物件添加了攔截器。在攔截器方法中,將遠端排程方法交給了ribbon的負載均衡器LoadBalancerClient去處理,從而達到負載均衡的目的。