Spring Cloud通過源碼深入解析Ribbon負載均衡器

2020-12-04 計算機java編程

為了深入理解Ribbon,現在從源碼的角度來講解Ribbon,看它如何和Eureka相結合,並如何和RestTemplate相結合實現負載均衡。首先跟蹤LoadBalancerClient的源碼,它是一個接口類,繼承了ServiceInstanceChooser,它的實現類為RibbonLoadBalancerClient,它們之間的關係如下圖:

LoadBalancerClient的父類和實現類

LoadBalancerClient是一個負載均衡的客戶端,有如下3個方法,其中2個execute()方法,均是用來執行請求的,reconstructURI()是用來重構URL的,代碼如下:

LoadBalancerClient定義的3個方法

ServiceInstanceChooser接口有一個方法用於根據serviceId獲取ServiceInstance,即根據服務名來選擇服務實例,代碼如下:

LoadBalancerClient的實現類為RibbonLoadBalancerClient。RibbonLoadBalancerClient是一個非常重要的類,最終的負載均衡的處理由它來處理。RibbonLoadBalancerClient的部分源碼如下:

RibbonLoadBalancerClient的源碼中,choose()方法用來選擇具體的服務實例。該方法通過getServer()方法去獲取實例,經過源碼跟蹤,最終交給ILoadBalancer類去選擇服務實例。

ILoadBalancer在Ribbon-loadbalancer的jar包下,ILoadBalancer是一個接口,該接口定義了一系列實現負載均衡的方法,源碼如下:

addServers():用於添加一個Server集合。chooseServer():用於根據key去獲取Server。markServerDown():用於標記某個服務下線。getReachableServers():獲取可用的Server集合。getAllServers():獲取所有的Server集合。

ILoadBalancer的子類為BaseLoadBalancer,BaseLoadBalancer的實現類為DynamicServerListLoadBalancer,三者之間的關係如下圖:

查看DynamicServerListLoadBalancer類的源碼,DynamicServerListLoadBalancer需要配置IClientConfig、IRule、IPing、ServerList、ServerListFilter和ILoadBalancer。查看BaseLoadBalancer類的源碼,在默認的情況下,實現了如下配置。

IClientConfig ribbonIClientConfig:DefaultIClientConfigImplIRule ribbonRule:RoundRibbonRuleIPing ribbonPing:DummyPingServerList ribbonServerList:ConfigurationBasedServerListServerListFilter ribbonServerListFilter:ZonePreferenceServerListFilterILoadBalancer ribbonILoadBalancer:ZoneAwareLoadBalancer

IClientConfig用於配置負載均衡的客戶端,IClientConfig的默認實現類為DefaultIClientConfigImpl。

IRule用於配置負載均衡的策略,IRule有3個方法,其中choose()方法根據key來獲取server實例的,setLoadBalancer()和getLoadBalancer()是用來設置和獲取ILoadBalancer的,它的源碼如下:

IRule有很多的默認實現類,這些實現類根據不同的算法和邏輯來處理負載均衡的策略。IRule的默認實現類有以下7種。在大多數情況下,這些默認的實現類是可以滿足需求的,如果有特殊的需求,可以自己實現。IRule和實現類之間的關係圖如下。

BestAvailableRule:選擇最小請求值。ClientConfigEnableRoundRobinRule:輪循。RandomRule:隨機選擇一個server。RoundRobinRule:輪循選擇server。RetryRule:根據輪循的方式重試。WeightedResponseTimeRule:根據響應時間去分配一個weight,weight越低,被選擇的可能性就越低。ZoneAvoidanceRule:根據server的zone區域和可用性來輪循選擇。

IPing用於向server發送「ping」,來判斷server是否有響應,從而判斷該server是否可用。它有一個isAlive()方法,源碼如下:

IPing的實現類有PingUrl、PingConstant、NoOpPing、DummyPing和NIWSDiscoveryPing。他們之間的關係如下圖:

PingUrl:真實地去ping某個URL,判斷其是否可用。PingConstant:固定返回某服務是否可用,默認返回true,即可用。NoOpPing:不去ping,直接返回true,即可用。DummyPing:直接返回true,並實現了initWithNiewsConfig()方法。NIWSDiscoveryPing:根據DiscoveryEnableServer的InstanceInfo的InstanceStatus去判斷,如果為InstanceStatus.UP,則可用,否則不可用。

ServerList是定義獲取所有server的註冊列表信息的接口,它的代碼如下:

ServerListFilter接口定義了可根據配置去過濾或者特性動態的獲取符合條件的server列表的方法,代碼如下:

閱讀DynamicServerListLoadBalancer的源碼,DynamicServerListLoadBalancer的構造函數中有一個initWithNiewsConfig()方法。在該方法中經過一系列的初始化配置,最終執行了restOfInit()方法。DynamicServerListLoadBalancer的部分源碼如下:

在restOfInit()方法中,有一個updateListOfServers()的方法,該方法是用來獲取所有的ServerList的。

進一步跟蹤updateListOfServers()方法的源碼,最終由serverListImpl.getUpdatedListOfServers()獲取所有的服務列表,代碼如下:

而serverListImpl是ServerList接口的具體實現類。跟蹤源碼,ServerList的實現類為DiscoveryEnableNIEWSServerList,這個類在ribbon-eureka.jar的com.netflix.niews.loadbalancer包下。其中,DiscoveryEnableNIEWSServerList有getInitialListOfServers()和getUpdatedListOfServers()的方法,具體代碼如下:

繼續跟蹤源碼,obtainServersViaDiscovery()方法是根據eurekaClientProvider.get()方法來獲取EurekaClient的,在根據EurekaClient來獲取服務註冊表列信息,代碼如下:

其中,eurekaClientProvider的實現類是LegacyEurekaClientProvider,LegacyEurekaClientProvider是一個獲取eurekaClient實例的類,其中代碼如下:

EurekaClient的實現類為DiscoveryClient,DiscoveryClient具有服務註冊、獲取服務註冊列表等功能。

由此可見,負載均衡是從Eureka Client獲取服務列表信息的,並根據IRule的策略去路由,根據IPing去判斷服務的可用性。

那麼還有一個問題,負載均衡器沒個多長時間從Eureka Client獲取註冊信息呢?

在BaseLoadBalancer類的源碼中,在BaseLoadBalancer的構造方法開啟了一個PingTask任務,代碼如下:

在setupPingTask()的具體代碼邏輯裡,開啟了ShutdownEnabledTimer的PingTask任務,在默認情況下,變量pingIntervalSeconds的值為10,即每10秒向Eureka Client發送一次心跳「ping」。

在PingTask的源碼,PingTask創建了一個Pinger對象,並執行了runPinger()方法。

查看Pinger的runPinger()方法,最終根據pingerStrategy.pingServers(ping, allServers)來獲取服務的可用性,如果該返回結果與之前相同,則不向Eureka Client獲取註冊列表;如果不同,則通知ServerStatusChangeListener服務註冊列表信息發生了改變,進行更新或者重新拉取,代碼如下:

由此可見,LoadBalancerClient是在初始化時向Eureka獲取服務註冊列表信息,並且每10秒向EurekaClient發送「ping」,來判斷服務的可用性。如果服務的可用性發生了改變或者服務的數量和之前不一致,則更新或者重新拉取。LoadBalancerClient有了這些服務註冊列表信息,就可以根據具體IRule的策略來進行負載均衡。

最後,回到問題的本身,為什麼在RestTemplate類的bean上加上一個@LoadBalanced註解就可以使用Ribbon的負載均衡呢?

全局搜索查看哪些類使用了@LoadBalanced註解。通過搜索,可以發現LoadBalancerAutoConfiguration類(LoadBalancer的自動配置類)使用到了該註解,LoadBalancerAutoConfiguration類的代碼如下:

在LoadBalancerAutoConfiguration類中,首先維護了一個被@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加上@LoadBalanced註解後,在遠程調度時能夠負載均衡,主要維護了一個被@LoadBalanced註解的RestTemplate列表,並給該列表中的RestTemplate對象添加了攔截器。在攔截器的方法中,將遠程調度方法交給了Ribbon的負載均衡器LoadBalancerClient去處理,從而達到了負載均衡的目的。

相關焦點

  • 圖靈學院:「微服務架構」SpringCloud之Ribbon(四)
    我們也很容易使用Ribbon實現自定義的負載均衡算法。注意我們只支持 URI 的 "/" 部分的路徑,一旦伺服器被負載均衡器選中,會由客戶端計算出完整的 URI;調用 API client.executeWithLoadBalancer(),不是 exeucte() API;動態修正配置中的伺服器池;等待伺服器列表刷新(配置文件中定義的刷新間隔是為
  • 「極簡SpringCloud」4,服務消費者(Ribbon版本)
    一般來講生產上都會啟動多個服務提供者,在消費者調用時,通過負載均衡選擇一個服務提供方。如果某個服務提供者宕機,可以切換到另外的服務提供者。保證服務的可用性。在5大組件一章中,我們說到Spring Cloud Ribbon是一個基於HTTP和TCP的客戶端負載均衡工具,它基於Netflix Ribbon實現。
  • SpringCloud微服務架構篇4:深入了解Ribbon與Feign
    01深入Ribbon1、Ribbon客戶端負載均衡原理伺服器列表過濾(ServerListFilter)該組件會對原始服務列表使用一定策略進行過濾,並返回有效的伺服器列表給客戶端負載均衡器使用。負載均衡策略(IRule)負責選擇一個最終服務實例地址作為負載均衡處理結果。Ribbon提供的選擇策略有輪詢、根據相應時間加權、斷路器(當Hystrix可用時)等。
  • SpringCloud2.0 精簡教程 第四課 Eureka Ribbon 服務消費端
    如何配置 基於 Ribbon 的服務消費端 集群第一步 創建 Spring Boot 基礎工程1.使用 IntelliJ IDEA 新建嚮導,創建 Spring Boot 工程2.工程名稱: springcloud-eureka-ribbon
  • 從零搭建 Spring Cloud 服務(超詳細)
    +ribbon ribbon是一種負載均衡的客戶端,它是什麼呢?請詳讀https://www.jianshu.com/p/1bd66db5dc46可以看見其中的一段如下:而客戶端負載均衡和服務端負載均衡最大的不同點在於上面所提到服務清單所存儲的位置。
  • SpringCloud服務間通信方式
    總結:在springcloud中服務間調用方式主要是使用 http restful方式進行服務間調用1|0 1.總結rest Template是直接基於服務地址調用沒有在服務註冊中心獲取服務,也沒有辦法完成服務的負載均衡如果需要實現服務的負載均衡需要自己書寫服務負載均衡策略。
  • Java教程:ribbon的常用負載均衡算法分析
    1.Ribbon介紹因為微服務是目前網際網路公司比較流行的架構,所以spring就提供了一個頂級框架-spring cloud,來解決我們在開發微服務架構中遇到的各種各樣的問題,今天的主角是spring cloud 框架中集成的組件Ribbon,那麼Ribbon能解決什麼問題呢,我們來思考下面的問題。
  • 基於SpringBoot Cloud構建的一個商城項目源碼分享
    基於springboot cloud構建的一個商城項目,包括前端,後端和h5應用,小程序,作為zscat應用實踐的模板項目。基於SpringBoot2.x、SpringCloud和SpringCloudAlibaba並採用前後端分離的企業級微服務敏捷開發系統架構。
  • Alibaba內部通用PDF:Spring Cloud微服務構建+實戰+源碼深度解析
    這些優秀的源碼中有著多年積澱下來的精華,這些精華是非常值得我們學習的,不管我們當前是什麼水平,通過反覆閱讀源碼能力能有所提升,小到對源碼所提供的功能上的使用更加熟練,大到使我們的程序設計更加完美優秀。但是,縱觀我們身邊的人,能夠做到通讀源碼的真的是少之又少。
  • SpringCloud聲明式調用Fegin實戰
    spring cloud全家桶,包含了許多組件,滿足了大家開發過程中的大部分需求,使用起來非常方便。這裡我們來說一下服務之間的軟負載實現(Ribbon調用和Fegin調用)。其中指定spring.application.name的值都為service-1。
  • springcloud五大組件
    首先我們來看springcloud是什麼?它是微服務架構集大成者,基於springboot構建,可以將一系列優秀組件進行完美整合。對熟悉的程式設計師來說,上手不麻煩,對新手來說,就需要了解springcloud架構再去學習。
  • 什麼是Spring Cloud項目,我把它講清楚了
    01微服務講起springcloud之前,我們需要了解一下什麼是微服務。客戶端向註冊中心訂閱服務註冊中心向客戶端推送有效的服務信息客戶端得到所有可調用服務的信息後, 根據需求,按負載均衡算法, 進行調用, 獲取數據。下面用百度百科把SOA說得高大上點。
  • zuihou-admin-cloud 2.4 發布,微服務 SaaS 腳手架 - OSCHINA...
    修復zipkin + seata整合時衝突報錯10.廢棄SystemApi模塊11.依賴升級    spring.boot.version -> 2.2.7.RELEASE    spring.cloud.version -> Hoxton.SR3    dynamic.datasource.version -> 3.1.0    mybatis.version
  • Spring Cloud系列各子項目在微服務架構中的作用分析
    Springcloud一般用於搭建微服務項目,那麼微服務項目是怎麼工作的呢?Springcloud的幾個子項目分別扮演什麼角色?所以需要網關來提供一個統一的調用地址給前端調用,通過請求信息區分調用不同微服務,提供統一的路由定位轉發、請求過濾、負載均衡、請求響應修改等功能,在網關服務上做認證授權等公共處理。
  • 微服務之Ribbon的基本使用
    第一個問題就是當有多個用戶服務節點的時候,我們如何讓電影服務調用用戶服務實現負載均衡。第二個問題就是我們如何讓電影服務調用用戶服務的時候去使用Eureka Server裡面的信息,現在還是通過硬編碼去調用的。
  • 一分鐘了解負載均衡的一切
    ,是通過「DNS輪詢」實現的:DNS-server對於一個域名配置了多個解析ip,每次DNS解析請求來訪問DNS-server,會輪詢返回這些ip,保證每個ip的解析概率是相同的。,是通過「nginx」實現的。
  • springcloud第四節 搭建項目
    1、創建父項目cloud-master,然後創建一個工具類項目和三個微服務,對應上一節所創建的資料庫,用戶服務、訂單服務、商品服務:(具體的創建細節就不說了喲)2、首先啟動nacos,然後分別啟動三個微服務,此時會註冊到nacos中:3、負載均衡的測試:首先大家都知道
  • 源碼不止Spring!阿里首推源碼成長筆記,深入底層不再蒙圈
    咳咳咳不要想歪,是Spring+MyBatis源碼!!!別的不說,我想先問大家一個問題:不會有人真的喜歡看源碼吧!在我的認知裡喜歡看源碼的都是「變態」,都是大哥!現實中大多數人都是為了漲薪或者一些其他的原因才會去主動接觸源碼(手動狗頭)。但是在現在網際網路大勢所趨下,源碼閱讀已經是一個優秀軟體開發者必備的能力。
  • 美團T9都說太「強」了,以微服務分布式的實戰詳解SpringCloud
    從企業的真實需求出發,理論結合實際,深入講解SpringCloud微服務和分布式系統的知識。既包括SpringCloud微服務的各類常用組件的講解,又包括分布式系統的常用知識的介紹。本書的實踐部分通過Apache Thrift 講解了遠程過程調用(RPC)在分布式系統中的應用,並且分析了處理高並發的一些常用方法,最後還通過一個簡單的實例講解了微服務系統的搭建。