天天看點

Spring Cloud Alibaba之負載均衡元件 - Ribbon

我們都知道在微服務架構中,微服務之間總是需要互相調用,以此來實作一些組合業務的需求。例如組裝訂單詳情資料,由于訂單詳情裡有使用者資訊,是以訂單服務就得調用使用者服務來擷取使用者資訊。要實作遠端調用就需要發送網絡請求,而每個微服務都可能會存在有多個執行個體分布在不同的機器上,那麼當一個微服務調用另一個微服務的時候就需要将請求均勻的分發到各個執行個體上,以此避免某些執行個體負載過高,某些執行個體又太空閑,是以在這種場景必須要有負載均衡器。

目前實作負載均衡主要的兩種方式:

1、服務端負載均衡;例如最經典的使用Nginx做負載均衡器。使用者的請求先發送到Nginx,然後再由Nginx通過配置好的負載均衡算法将請求分發到各個執行個體上,由于需要作為一個服務部署在服務端,是以該種方式稱為服務端負載均衡。如圖:

Spring Cloud Alibaba之負載均衡元件 - Ribbon

2、用戶端側負載均衡;之是以稱為用戶端側負載均衡,是因為這種負載均衡方式是由發送請求的用戶端來實作的,也是目前微服務架構中用于均衡服務之間調用請求的常用負載均衡方式。因為采用這種方式的話服務之間可以直接進行調用,無需再通過一個專門的負載均衡器,這樣能夠提高一定的性能以及高可用性。以微服務A調用微服務B舉例,簡單來說就是微服務A先通過服務發現元件擷取微服務B所有執行個體的調用位址,然後通過本地實作的負載均衡算法選取出其中一個調用位址進行請求。如圖:

Spring Cloud Alibaba之負載均衡元件 - Ribbon

我們來通過Spring Cloud提供的DiscoveryClient寫一個非常簡單的用戶端側負載均衡器,借此直覺的了解一下該種負載均衡器的工作流程,該示例中采用的負載均衡政策為随機,代碼如下:

什麼是Ribbon:

Ribbon是Netflix開源的用戶端側負載均衡器

Ribbon内置了非常豐富的負載均衡政策算法

Ribbon雖然是個主要用于負載均衡的小元件,但是麻雀雖小五髒俱全,Ribbon還是有許多的接口元件的。如下表:

Spring Cloud Alibaba之負載均衡元件 - Ribbon

Ribbon預設内置了八種負載均衡政策,若想自定義負載均衡政策則實作上表中提到的IRule接口或AbstractLoadBalancerRule抽象類即可。内置的負載均衡政策如下:

Spring Cloud Alibaba之負載均衡元件 - Ribbon

預設的政策規則為ZoneAvoidanceRule

Ribbon主要有兩種使用方式,一是使用Feign,Feign内部已經整合了Ribbon,是以如果隻是普通使用的話都感覺不到Ribbon的存在;二是配合RestTemplate使用,這種方式則需要添加Ribbon依賴和@LoadBalanced注解。

這裡主要示範一下第二種使用方式,由于項目中添加的Nacos依賴已包含了Ribbon是以不需要另外添加依賴,首先定義一個RestTemplate,代碼如下:

然後使用RestTemplate調用其他服務的時候,隻需要寫服務名即可,不需要再寫ip位址和端口号。如下示例:

如果不太清楚RestTemplate的使用,可以參考如下文章:

微服務之間的通信的方式

在實際開發中,我們可能會遇到預設的負載均衡政策無法滿足需求,進而需要更換其他的負載均衡政策。關于Ribbon負載均衡的配置方式主要有兩種,在代碼中配置或在配置檔案中配置。

Ribbon支援細粒度的配置,例如我希望微服務A在調用微服務B的時候采用随機的負載均衡政策,而在調用微服務C的時候采用預設政策,下面我們就來實作一下這種細粒度的配置。

1、首先是通過代碼進行配置,編寫一個配置類用于執行個體化指定的負載均衡政策對象:

然後再編寫一個用于配置Ribbon用戶端的配置類,該配置類的目的是指定在調用user-center時采用RibbonConfig裡配置的負載均衡政策,這樣就可以達到細粒度配置的效果:

需要注意的是RibbonConfig應該定義在主啟動類之外,避免被Spring掃描到,不然會産生父子上下文掃描重疊的問題,進而導緻各種奇葩的問題。而在Ribbon這裡就會導緻該配置類被所有的Ribbon用戶端共享,即不管調用user-center還是其他微服務都會采用該配置類裡定義的負載均衡政策,這樣就會變成了一個全局配置了,違背了我們需要細粒度配置的目的。是以需要将其定義在主啟動類之外:

Spring Cloud Alibaba之負載均衡元件 - Ribbon

關于這個問題可以參考官方文檔的描述:

https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#_customizing_the_ribbon_client

2、使用配置檔案進行配置就更簡單了,不需要寫代碼還不會有父子上下文掃描重疊的坑,隻需在配置檔案中增加如下一段配置就可以實作以上使用代碼配置等價的效果:

兩種配置方式對比:

Spring Cloud Alibaba之負載均衡元件 - Ribbon

關于優先級:細粒度配置檔案配置 > 細粒度代碼配置 > 全局配置檔案配置 > 全局代碼配置

最佳實踐總結:

盡量使用配置檔案配置,配置檔案滿足不了需求的情況下再考慮使用代碼配置

在同一個微服務内盡量保持單一性,例如統一使用配置檔案配置,盡量不要兩種方式混用,以免增加定位問題的複雜度

以上介紹的是細粒度地針對某個特定Ribbon用戶端的配置,下面我們再示範一下如何實作全局配置。很簡單,隻需要把注解改為@RibbonClients即可,代碼如下:

Ribbon預設是懶加載的,是以在第一次發生請求的時候會顯得比較慢,我們可以通過在配置檔案中添加如下配置開啟饑餓加載:

以上小節基本介紹完了負載均衡及Ribbon的基礎使用,接下來的内容需要配合Nacos,若沒有了解過Nacos的話可以參考以下文章:

Spring Cloud Alibaba之服務發現元件 - Nacos

在Nacos Server的控制台頁面可以編輯每個微服務執行個體的權重,服務清單 -> 詳情 -> 編輯;預設權重都為1,權重值越大就越優先被調用:

Spring Cloud Alibaba之負載均衡元件 - Ribbon

權重在很多場景下非常有用,例如一個微服務有很多的執行個體,它們被部署在不同配置的機器上,這時候就可以将配置較差的機器上所部署的執行個體權重設定得比較低,而部署在配置較好的機器上的執行個體權重設定得高一些,這樣就可以将較大一部分的請求都分發到性能較高的機器上。

但是Ribbon内置的負載均衡政策都不支援Nacos的權重,是以我們就需要自定義實作一個支援Nacos權重配置的負載均衡政策。好在Nacos Client已經内置了負載均衡的能力,是以實作起來也比較簡單,代碼如下:

然後在配置檔案中配置一下就可以使用該負載均衡政策了:

思考:既然Nacos Client已經有負載均衡的能力,Spring Cloud Alibaba為什麼還要去整合Ribbon呢?

個人認為,這主要是為了符合Spring Cloud标準。Spring Cloud Commons有個子項目 spring-cloud-loadbalancer ,該項目制定了标準,用來适配各種用戶端負載均衡器(雖然目前實作隻有Ribbon,但Hoxton就會有替代的實作了)。 Spring Cloud Alibaba遵循了這一标準,是以整合了Ribbon,而沒有去使用Nacos Client提供的負載均衡能力。

在Spring Cloud Alibaba之服務發現元件 - Nacos一文中已經介紹過叢集的概念以及作用,這裡就不再贅述,加上上一小節中已經介紹過如何自定義負載均衡政策了,是以這裡不再啰嗦而是直接上代碼,實作代碼如下:

同樣的,想要使用該負載均衡政策的話,在配置檔案中配置一下即可:

在以上兩個小節我們實作了基于Nacos權重的負載均衡政策及同一叢集下優先調用的負載均衡政策,但在實際項目中,可能會面臨多版本共存的問題,即一個微服務擁有不同版本的執行個體,并且這些不同版本的執行個體之間可能是互不相容的。例如微服務A的v1版本執行個體無法調用微服務B的v2版本執行個體,隻能夠調用微服務B的v1版本執行個體。

而Nacos中的中繼資料就比較适合解決這種版本控制的問題,至于中繼資料的概念及配置方式已經在Spring Cloud Alibaba之服務發現元件 - Nacos一文中介紹過,這裡主要介紹一下如何通過Ribbon去實作基于中繼資料的版本控制。

舉個例子,線上有兩個微服務,一個作為服務提供者一個作為服務消費者,它們都有不同版本的執行個體,如下:

服務提供者有兩個版本:v1、v2

服務消費者也有兩個版本:v1、v2

v1和v2是不相容的。服務消費者v1隻能調用服務提供者v1;消費者v2隻能調用提供者v2。如何實作呢?下面我們來圍繞該場景,實作微服務之間的版本控制。

綜上,我們需要實作的主要有兩點:

優先選擇同叢集下,符合metadata的執行個體

如果同叢集下沒有符合metadata的執行個體,就選擇其他叢集下符合metadata的執行個體

首先我們得在配置檔案中配置中繼資料,中繼資料就是一堆的描述資訊,以k - v形式進行配置,如下:

然後就可以寫代碼了,和之前一樣,也是通過負載均衡政策實作,具體代碼如下:

繼續閱讀