Ribbon核心源碼解析(二)- ILoadBalancer組件

      網友投稿 1102 2022-05-30

      在上一篇文章中,我們介紹了Ribbon中的調用流程與負載均衡過程,本文我們再來詳細看一看它的核心組件ILoadBalancer。

      核心組件ILoadBalancer

      返回服務實例的調用過程大體已經了解了,但是我們剛才略過了一個內容,就是獲取LoadBalancer的過程,回去看第一次調用的getServer方法:

      這里通過getLoadBalancer方法返回一個ILoadBalancer負載均衡器,具體調用了Spring的BeanFactoryUtil,通過getBean方法從spring容器中獲取類型匹配的bean實例:

      回到前面getServer方法調用的那張圖,你就會發現這時候已經返回了一個ZoneAwareLoadBalancer,并且其中已經保存好了服務列表。看一下ILoadBalancer 的接口定義:

      public interface ILoadBalancer { //往該ILoadBalancer中添加服務 public void addServers(List newServers); //選擇一個可以調用的實例,keyb不是服務名稱,而是zone的id public Server chooseServer(Object key); //標記下線服務 public void markServerDown(Server server); @Deprecated public List getServerList(boolean availableOnly); //獲取可用服務列表 public List getReachableServers(); //獲取所有服務列表 public List getAllServers(); }

      該接口定義了Ribbon中核心的兩項內容,服務獲取與服務選擇,可以說,ILoadBalancer是Ribbon中最重要的一個組件,它起到了承上啟下的作用,既要連接 Eureka獲取服務地址,又要調用IRule利用負載均衡算法選擇服務。下面分別介紹。

      服務獲取

      Ribbon在選擇之前需要獲取服務列表,而Ribbon本身不具有服務發現的功能,所以需要借助Eureka來解決獲取服務列表的問題。回到文章開頭說到的配置類RibbonEurekaAutoConfiguration:

      @Configuration @EnableConfigurationProperties @ConditionalOnRibbonAndEurekaEnabled @AutoConfigureAfter(RibbonAutoConfiguration.class) @RibbonClients(defaultConfiguration = EurekaRibbonClientConfiguration.class) public class RibbonEurekaAutoConfiguration { }

      其中定義了其默認配置類為EurekaRibbonClientConfiguration,在它的ribbonServerList方法中創建了服務發現組件DiscoveryEnabledNIWSServerList:

      DiscoveryEnabledNIWSServerList實現了ServerList接口,該接口用于初始化服務列表及更新服務列表。首先看一下ServerList的接口定義,其中兩個方法分別用于初始化服務列表及更新服務列表:

      public interface ServerList { public List getInitialListOfServers(); public List getUpdatedListOfServers(); }

      在DiscoveryEnabledNIWSServerList中,初始化與更新兩個方法其實調用了同一個方法來實現具體邏輯:

      進入obtainServersViaDiscovery方法:

      可以看到,這里先得到一個EurekaClient的實例,然后借助EurekaClient的服務發現功能,來獲取服務的實例列表。在獲取了實例信息后,判斷服務的狀態如果為UP,那么最終將它加入serverList中。

      Ribbon核心源碼解析(二)- ILoadBalancer組件

      在獲取得到serverList后,會進行緩存操作。首先進入DynamicServerListLoadBalancer的setServerList方法,然后調用父類BaseLoadBalancer的setServersList方法:

      在BaseLoadBalancer中,定義了兩個緩存列表:

      protected volatile List allServerList = Collections.synchronizedList(new ArrayList()); protected volatile List upServerList = Collections.synchronizedList(new ArrayList());

      在父類的setServersList中,將拉取的serverList賦值給緩存列表allServerList:

      在Ribbon從Eureka中得到了服務列表,緩存在本地List后,存在一個問題,如何保證在調用服務的時候服務仍然處于可用狀態,也就是說應該如何解決緩存列表臟讀問題?

      在默認負載均衡器ZoneAwareLoadBalancer的父類BaseLoadBalancer構造方法中,調用setupPingTask方法,并在其中創建了一個定時任務,使用ping的方式判斷服務是否可用:

      runPinger方法中,調用SerialPingStrategy的pingServers方法:

      pingServers方法中,調用NIWSDiscoveryPing的isAlive方法:

      NIWSDiscoveryPing實現了IPing接口,在IPing 接口中,僅有一個isAlive方法用來判斷服務是否可用:

      public interface IPing { public boolean isAlive(Server server); }

      NIWSDiscoveryPing的isAlive方法實現:

      因為本地的serverList為緩存值,可能與eureka中不同,所以從eureka中去查詢該實例的狀態,如果eureka里面顯示該實例狀態為UP,就返回true,說明服務可用。

      返回Pinger的runPingger的方法調用處:

      在獲取到服務的狀態列表后進行循環,如果狀態改變,加入到changedServers中,并且把所有可用服務加入newUpList,最終更新upServerList中緩存值。但是在閱讀源碼中發現,創建了一個-用于監聽changedServers這一列表,但是只是一個空殼方法,并沒有實際代碼對列表變動做出實際操作。

      需要注意的是,在調試過程中當我下線一個服務后,results數組并沒有按照預期的將其中一個服務的狀態返回為false,而是results數組中的元素只剩下了一個,也就說明,除了使用ping的方式去檢測服務是否在線外,Ribbon還使用了別的方式來更新服務列表。

      我們在BaseLoadBalancer的setServersList方法中添加一個斷點:

      等待程序運行,可以發現,在還沒有進入執行IPing的定時任務前,已經將下線服務剔除,只剩下了一個可用服務。查看調用鏈,最終可以發現使用了定時調度線程池調用了PollingServerListUpdater類的start方法,來進行更新服務操作:

      回到BaseLoadBalancer的setServersList方法中:

      在這里就用新的服務列表更新了舊服務列表,因此當執行IPing的線程再執行時,服務列表中只剩下了一個服務實例。

      綜上可以發現,Ribbon為了解決服務列表的臟讀現象,采用了兩種手段:

      更新列表

      ping機制

      在測試中發現,更新機制和ping機制功能基本重合,并且在ping的時候不能執行更新,在更新的時候不能運行ping,所以很難檢測到ping失敗的情況。

      服務選取

      服務選取的過程就是從服務列表中按照約定規則選取服務實例,與負載均衡算法相關。這里引入Ribbon對于負載均衡策略實現的接口IRule:

      public interface IRule{ public Server choose(Object key); public void setLoadBalancer(ILoadBalancer lb); public ILoadBalancer getLoadBalancer(); }

      其中choose為核心方法,用于實現具體的選擇邏輯。

      Ribbon中,下面7個類默認實現了IRule接口,為我們提供負載均衡算法:

      在剛才調試過程中,可以知道Ribbon默認使用的是ZoneAvoidanceRule區域親和負載均衡算法,優先調用一個zone區間中的服務,并使用輪詢算法,具體實現過程前面已經介紹過不再贅述。

      當然,也可以由我們自己實現IRule接口,重寫其中的choose方法來實現自己的負載均衡算法,然后通過@Bean的方式注入到spring容器中。當然也可以將不同的服務應用不同的IRule策略,這里需要注意的是,Spring cloud的官方文檔中提醒我們,如果多個微服務要調用不同的IRule,那么創建出IRule的配置類不能放在ComponentScan的目錄下面,這樣所有的微服務都會使用這一個策略。

      需要在主程序運行的com包外另外創建一個config包用于專門存放配置類,然后在啟動類上加上@RibbonClients注解,不同服務應用不同配置類:

      @RibbonClients({@RibbonClient(name="eureka-hi",configuration = HiRuleConfig.class), @RibbonClient(name = "eureka-test",configuration = TestRuleConfig.class)}) public class ServiceFeignApplication { …… }

      總結

      綜上所述,在Ribbon的負載均衡中,大致可以分為以下幾步:

      攔截請求,通過請求中的url地址,截取服務名稱

      通過LoadBalancerClient獲取ILoadBalancer

      使用Eureka獲取服務列表

      通過IRule負載均衡策略選擇具體服務

      ILoadBalancer通過IPing及定時更新機制來維護服務列表

      重構該url地址,最終調用HttpURLConnection發起請求

      了解了整個調用流程后,我們更容易明白為什么Ribbon叫做客戶端的負載均衡。與nginx服務端負載均衡不同,nginx在使用反向代理具體服務的時候,調用端不知道都有哪些服務。而Ribbon在調用之前,已經知道有哪些服務可用,直接通過本地負載均衡策略調用即可。而在實際使用過程中,也可以根據需要,結合兩種方式真正實現高可用。

      最后

      覺得對您有所幫助,小伙伴們可以點個贊啊,非常感謝~

      公眾號『碼農參上』,一個熱愛分享的公眾號,有趣、深入、直接,與你聊聊技術。歡迎來加我好友 DrHydra9,圍觀朋友圈,做個之交。

      Spring Spring Cloud 微服務 負載均衡緩存

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:Bluetooth Profile Specification之1.1 A2DP 之Audio Codec-SBC
      下一篇:基于華為搭建自己的MQTT服務器
      相關文章
      亚洲一区精品无码| 国产精品亚洲片在线花蝴蝶| 亚洲AV伊人久久青青草原| 亚洲色精品三区二区一区| 国产精品亚洲四区在线观看| 亚洲乱码一二三四五六区| 亚洲国产精品久久网午夜| 亚洲男人天堂影院| 亚洲无限乱码一二三四区| 亚洲第一页在线视频| 亚洲第一页在线视频| 亚洲午夜电影一区二区三区| 亚洲国产中文在线视频| 亚洲免费在线视频观看| 亚洲一区在线视频观看| 国产色在线|亚洲| 亚洲欧美日韩中文无线码| 亚洲精品国产精品| 精品亚洲福利一区二区| 国产亚洲视频在线观看| 亚洲一级特黄大片无码毛片| 亚洲人成77777在线播放网站| 亚洲欧洲∨国产一区二区三区| 亚洲伊人久久精品影院| 久久亚洲精品中文字幕三区| 久久久亚洲精品国产| 2022年亚洲午夜一区二区福利 | 亚洲精华液一二三产区| 亚洲国产成人无码AV在线影院| 在线亚洲午夜片AV大片| 精品国产_亚洲人成在线| 亚洲一区二区三区在线视频| 超清首页国产亚洲丝袜| 亚洲av永久无码精品网站| 亚洲综合视频在线观看| 亚洲男人天堂2022| 国产成人亚洲精品无码AV大片| 国产亚洲精品无码专区| 亚洲AV电影院在线观看| 91亚洲精品麻豆| 亚洲成aⅴ人片久青草影院按摩|