|
深入理解Ribbon之源碼解析23
發表時間:2018-09-05 11:51 什么是RibbonRibbon是Netflix公司開源的一個負載均衡的項目,它屬于上述的第二種,是一個客戶端負載均衡器,運行在客戶端上。它是一個經過了云端測試的IPC庫,可以很好地控制HTTP和TCP客戶端的一些行為。 Feign已經默認使用了Ribbon。
RestTemplate和Ribbon相結合Ribbon在Netflix組件是非常重要的一個組件,在Zuul中使用Ribbon做負載均衡,以及Feign組件的結合等。在Spring Cloud 中,作為開發中,做的最多的可能是將RestTemplate和Ribbon相結合,你可能會這樣寫: 消費另外一個的服務的接口,差不多是這樣的: 深入理解RibbonLoadBalancerClient在Riibon中一個非常重要的組件為LoadBalancerClient,它作為負載均衡的一個客戶端。它在spring-cloud-commons包下: 其中LoadBalancerClient接口,有如下三個方法,其中excute()為執行請求,reconstructURI()用來重構url: ServiceInstanceChooser接口,主要有一個方法,用來根據serviceId來獲取ServiceInstance,代碼如下: LoadBalancerClient的實現類為RibbonLoadBalancerClient,這個類是非常重要的一個類,最終的負載均衡的請求處理,由它來執行。它的部分源碼如下: 在RibbonLoadBalancerClient的源碼中,其中choose()方法是選擇具體服務實例的一個方法。該方法通過getServer()方法去獲取實例,經過源碼跟蹤,最終交給了ILoadBalancer類去選擇服務實例。 ILoadBalancer在ribbon-loadbalancer的jar包下,它是定義了實現軟件負載均衡的一個接口,它需要一組可供選擇的服務注冊列表信息,以及根據特定方法去選擇服務,它的源碼如下 : 其中,addServers()方法是添加一個Server集合;chooseServer()方法是根據key去獲取Server;markServerDown()方法用來標記某個服務下線;getReachableServers()獲取可用的Server集合;getAllServers()獲取所有的Server集合。 DynamicServerListLoadBalancer它的繼承類為BaseLoadBalancer,它的實現類為DynamicServerListLoadBalancer,這三者之間的關系如下: 查看上述三個類的源碼,可用發現,配置以下信息,IClientConfig、IRule、IPing、ServerList、ServerListFilter和ILoadBalancer,查看BaseLoadBalancer類,它默認的情況下,實現了以下配置:
IClientConfig 用于對客戶端或者負載均衡的配置,它的默認實現類為DefaultClientConfigImpl。 IRule用于復雜均衡的策略,它有三個方法,其中choose()是根據key 來獲取server,setLoadBalancer()和getLoadBalancer()是用來設置和獲取ILoadBalancer的,它的源碼如下: IRule有很多默認的實現類,這些實現類根據不同的算法和邏輯來處理負載均衡。Ribbon實現的IRule有一下。在大多數情況下,這些默認的實現類是可以滿足需求的,如果有特性的需求,可以自己實現。
IPing是用來想server發生”ping”,來判斷該server是否有響應,從而判斷該server是否可用。它有一個isAlive()方法,它的源碼如下: IPing的實現類有PingUrl、PingConstant、NoOpPing、DummyPing和NIWSDiscoveryPing。它門之間的關系如下:
ServerList是定義獲取所有的server的注冊列表信息的接口,它的代碼如下: ServerListFilter接口,定于了可根據配置去過濾或者根據特性動態獲取符合條件的server列表的方法,代碼如下: 閱讀DynamicServerListLoadBalancer的源碼,DynamicServerListLoadBalancer的構造函數中有個initWithNiwsConfig()方法。在改方法中,經過一系列的初始化配置,最終執行了restOfInit()方法。其代碼如下: 在restOfInit()方法上,有一個 updateListOfServers()的方法,該方法是用來獲取所有的ServerList的。 進一步跟蹤updateListOfServers()方法的源碼,最終由serverListImpl.getUpdatedListOfServers()獲取所有的服務列表的,代碼如下: 而serverListImpl是ServerList接口的具體實現類。跟蹤代碼,ServerList的實現類為DiscoveryEnabledNIWSServerList,在ribbon-eureka.jar的com.netflix.niws.loadbalancer下。其中DiscoveryEnabledNIWSServerList有 getInitialListOfServers()和getUpdatedListOfServers()方法,具體代碼如下: 繼續跟蹤源碼,obtainServersViaDiscovery(),是根據eurekaClientProvider.get()來回去EurekaClient,再根據EurekaClient來獲取注冊列表信息,代碼如下: 其中eurekaClientProvider的實現類是LegacyEurekaClientProvider,它是一個獲取eurekaClient類,通過靜態的方法去獲取eurekaClient,其代碼如下: EurekaClient的實現類為DiscoveryClient,在之前已經分析了它具有服務注冊、獲取服務注冊列表等的全部功能。 由此可見,負載均衡器是從EurekaClient獲取服務信息,并根據IRule去路由,并且根據IPing去判斷服務的可用性。 那么現在還有個問題,負載均衡器多久一次去獲取一次從Eureka Client獲取注冊信息呢。 在BaseLoadBalancer類下,BaseLoadBalancer的構造函數,該構造函數開啟了一個PingTask任務,代碼如下: setupPingTask()的具體代碼邏輯,它開啟了ShutdownEnabledTimer執行PingTask任務,在默認情況下pingIntervalSeconds為10,即每10秒鐘,想EurekaClient發送一次”ping”。 PingTask源碼,即new一個Pinger對象,并執行runPinger()方法。 查看Pinger的runPinger()方法,最終根據 pingerStrategy.pingServers(ping, allServers)來獲取服務的可用性,如果該返回結果,如之前相同,則不去向EurekaClient獲取注冊列表,如果不同則通知ServerStatusChangeListener或者changeListeners發生了改變,進行更新或者重新拉取。 由此可見,LoadBalancerClient是在初始化的時候,會向Eureka回去服務注冊列表,并且向通過10s一次向EurekaClient發送“ping”,來判斷服務的可用性,如果服務的可用性發生了改變或者服務數量和之前的不一致,則更新或者重新拉取。LoadBalancerClient有了這些服務注冊列表,就可以根據具體的IRule來進行負載均衡。 RestTemplate是如何和Ribbon結合的最后,回答問題的本質,為什么在RestTemplate加一個@LoadBalance注解就可可以開啟負載均衡呢? 全局搜索ctr+shift+f @LoadBalanced有哪些類用到了LoadBalanced有哪些類用到了, 發現LoadBalancerAutoConfiguration類,即LoadBalancer自動配置類。 在該類中,首先維護了一個被@LoadBalanced修飾的RestTemplate對象的List,在初始化的過程中,通過調用customizer.customize(restTemplate)方法來給RestTemplate增加攔截器LoadBalancerInterceptor。 而LoadBalancerInterceptor,用于實時攔截,在LoadBalancerInterceptor這里實現來負載均衡。LoadBalancerInterceptor的攔截方法如下: 總結綜上所述,Ribbon的負載均衡,主要通過LoadBalancerClient來實現的,而LoadBalancerClient具體交給了ILoadBalancer來處理,ILoadBalancer通過配置IRule、IPing等信息,并向EurekaClient獲取注冊列表的信息,并默認10秒一次向EurekaClient發送“ping”,進而檢查是否更新服務列表,最后,得到注冊列表后,ILoadBalancer根據IRule的策略進行負載均衡。 而RestTemplate 被@LoadBalance注解后,能過用負載均衡,主要是維護了一個被@LoadBalance注解的RestTemplate列表,并給列表中的RestTemplate添加攔截器,進而交給負載均衡器去處理。 |