1.Ribbon負載均衡簡介
1.1Ribbon概述
1.1.1.Ribbon是什麼
SpringCloud Ribbon是基於Netflix Ribbon實現的一套客戶端負載均衡的工具。
簡單的說,Ribbon是Netflix發佈的開源項目,主要功能是提供客戶端的軟件負載均衡算法,將Netflix的中間層服務連接在一起。Ribbon客戶端組件提供一系列完善的配置項如連接超時,重試等。簡單的說,就是在配置文件中列出LoadBalanCer(簡稱LB)後面所有的機器,Ribbon會自動的幫助你基於某種規則(如簡單輪詢,隨機連接等)去連接這些機器。我們也很容易使用Ribbon實現自定義的負載均衡算法。
1.1.2.Ribbon主要職責
LB(負載均衡)
LB,即負載均衡( Load Balanoe ),在微服務或分佈式集羣中經常用的一種應用。
負載均衡簡單的說就是將用戶的請求平攤的分配到多個服務上,從而達到系統的HA。
常見的負載均衡有軟件nginx , LVS,硬件F5等。
相應的在中間件,例如:dubbo和 SpringCloud中均給我們提供了負載均衡,SpringCloud的負載均衡算法可以自定義。
LB又分爲兩種,集中式LB和進程內LB
集中式LB(偏硬件)
即在服務的消費方和提供方之間使用獨立的LB設施(可以是硬件,如F5,也可以是軟件,如nginx ) ,由該設施負責把訪問請求通過某種策略轉發至服務的提供方;
進程內LB(偏軟件)
將LB邏輯集成到消費方,消費方從服務註冊中心獲知有哪些地址可用,然後自己再從這些地址中選擇出一個合適的服務器。
Ribbon就屬於進程內LB,它只是一個類庫,集成於消費方進程,消費方通過它來獲取到服各提供方的地址。
1.1.3 官方資料
https://github.com/Netflix/ribbon/wiki
2.Ribbon實例
上一篇的案例中,我們啓動了一個springcloud-demo,然後通過DiscoveryClient來獲取服務實例信息,然後獲取ip和端口來訪問。
但是實際環境中,我們往往會開啓很多個user-service的集羣。此時我們獲取的服務列表中就會有多個,到底該訪問哪一個呢?
一般這種情況下我們就需要編寫負載均衡算法,在多個實例列表中進行選擇。不過Eureka中已經幫我們集成了負載均衡組件:Ribbon,簡單修改代碼即可使用。
接下來,我們就來使用Ribbon實現負載均衡。
2.1.Ribbon架構說明
Ribbon 在工作時分成兩步:
第一步先選擇 EurekaServer,它優先選擇在同一個區域內負載較少的server。
第二步再根據用戶指定的策略,在從server取到的服務註冊列表中選擇一個地址。
其中Ribbon 提供了多種策略:比如輪詢、隨機和根據響應時間加權。
2.2.啓動兩個服務實例
首先我們啓動兩個springcloud-demo實例,一個80,一個81。
Eureka監控面板:
2.3.開啓負載均衡
因爲Eureka中已經集成了Ribbon,所以我們無需引入新的依賴
加入Ribbon的配置
# EurekaServer地址
eureka.client.service-url.defaultZone=http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
在RestTemplate的配置方法上添加@LoadBalanced
註解:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
修改調用方式,不再手動獲取ip和端口,而是直接通過服務名稱調用:
@RestController
@RequestMapping("consumer")
public class ConsumerController {
//改成下面這行
private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-DEMO-SERVICE";
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/test")
public List<User> consumerTest(){
return this.restTemplate.getForObject(REST_URL_PREFIX+"/all",List.class);
}
}
測試:
Ribbon整合後可以直接調用微服務而不再關心地址和端口!!!
2.4.源碼跟蹤
爲什麼我們只輸入了service名稱就可以訪問了呢?之前還要獲取ip和端口。顯然有人幫我們根據service名稱,獲取到了服務實例的ip和端口。它就是LoadBalancerInterceptor
我們進行源碼跟蹤:
繼續跟入execute方法:發現獲取了8001端口的服務
org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient
如果再跟下一次,發現獲取的是8002
2.5.負載均衡策略
Ribbon默認的負載均衡策略是簡單的輪詢,我們可以測試一下:
編寫測試類,在剛纔的源碼中我們看到攔截中是使用RibbonLoadBalanceClient
來進行負載均衡的,其中有一個choose
方法,是這樣介紹的:
現在這個就是負載均衡獲取實例的方法。我們對注入這個類的對象,然後對其測試:
package com.bruceliu.test;
import com.bruceliu.SpringcloudDemoConsumerApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringcloudDemoConsumerApplication.class)
public class LoadBalanceTest {
@Autowired
RibbonLoadBalancerClient client;
@Test
public void test1(){
for (int i = 0; i <10 ; i++) {
ServiceInstance instance = this.client.choose("SPRINGCLOUD-DEMO-SERVICE");
System.out.println(instance.getHost() + ":" + instance.getPort());
}
}
}
運行結果:
符合了我們的預期推測,確實是輪詢方式。
我們是否可以修改負載均衡的策略呢?繼續跟蹤源碼,發現這麼一段代碼:
我們看看這個rule是誰:
這裏的rule默認值是一個RoundRobinRule
,看類的介紹:
這不就是輪詢的意思嘛。
我們注意到,這個類其實是實現了接口IRule的,查看一下:
定義負載均衡的規則接口。
它有以下實現:
SpringBoot也幫我們提供了修改負載均衡規則的配置入口:
SPRINGCLOUD-DEMO-SERVICE:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
格式是:{服務名稱}.ribbon.NFLoadBalancerRuleClassName
,值就是IRule的實現類。
再次測試,發現結果變成了隨機:
2.6.重試機制
我們現在關閉一個springcloud-demo-8001實例
因爲服務剔除的延遲,consumer並不會立即得到最新的服務列表,此時再次訪問你會得到錯誤提示:
但是此時,8002服務其實是正常的。
因此Spring Cloud 整合了Spring Retry 來增強RestTemplate的重試能力,當一次服務調用失敗後,不會立即拋出一次,而是再次重試另一個服務。
只需要簡單配置即可實現Ribbon的重試:
spring:
cloud:
loadbalancer:
retry:
enabled: true # 開啓Spring Cloud的重試功能
SPRINGCLOUD-DEMO-SERVICE:
ribbon:
ConnectTimeout: 250 # Ribbon的連接超時時間
ReadTimeout: 1000 # Ribbon的數據讀取超時時間
OkToRetryOnAllOperations: true # 是否對所有操作都進行重試
MaxAutoRetriesNextServer: 1 # 切換實例的重試次數
MaxAutoRetries: 1 # 對當前實例的重試次數
根據如上配置,當訪問到某個服務超時後,它會再次嘗試訪問下一個服務實例,如果不行就再換一個實例,如果不行,則返回失敗。切換次數取決於MaxAutoRetriesNextServer
參數的值
引入spring-retry依賴
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
我們重啓springcloud-demo-consumer,測試,發現即使springcloud-demo-8001宕機,也能通過另一臺服務實例獲取到結果!
2.7 Ribbon核心組件IRule(面試題)
策略名稱 | 策略對應的類名 | 實現原理 |
---|---|---|
輪詢策略(默認) | RoundRobinRule | 輪詢策略表示每次都順序取下一個 provider,比如一共有 5 個provider,第 1 次取第 1 個,第 2次取第 2 個,第 3 次取第 3 個,以此類推 |
權重輪詢策略 | WeightedResponseTimeRule | 1.根據每個 provider 的響應時間分配一個權重,響應時間越長,權重越小,被選中的可能性越低。2.原理:一開始爲輪詢策略,並開啓一個計時器,每 30 秒收集一次每個 provider 的平均響應時間,當信息足夠時,每個 provider附上一個權重,並按權重隨機選擇provider,高權越重的 provider會被高概率選中。 |
隨機策略 | RandomRule | 從 provider 列表中隨機選擇一個provider |
最少併發數策略 | BestAvailableRule | 選擇正在請求中的併發數最小的 provider,除非這個provider 在熔斷中。 |
在“選定的負載均衡策略”基礎上進行重試機制 | RetryRule | 1.“選定的負載均衡策略”這個策略是輪詢策略RoundRobinRule 2.該重試策略先設定一個閾值時間段,如果在這個閾值時間段內當選擇 provider 不成功,則一直嘗試採用“選定的負載均衡策略:輪詢策略”最後選擇一個可用的provider |
可用性敏感策略 | AvailabilityFilteringRule | 過濾性能差的 provider,有 2種:第一種:過濾掉在 eureka 中處於一直連接失敗 provider 第二種:過濾掉高併發的 provider |
區域敏感性策略 | ZoneAvoidanceRule | 1.以一個區域爲單位考察可用性,對於不可用的區域整個丟棄,從剩下區域中選可用的provider2.如果這個 ip 區域內有一個或多個實例不可達或響應變慢,都會降低該 ip 區域內其他 ip 被選中的權重。機架! |
Ribbon的負載均衡算法內置了7種,如果還不能滿足實際的要求,那麼你需要自定義負載均衡算法~~~~~
寫一個類實現IRUle接口,重寫其中的方法
然後把自己的規則類,注入到系統中.....
2.8 修改訪問服務的算法方式
方式一:修改代碼更換負載均衡策略
在啓動類中添加實例負載均衡的實例,則默認的輪詢策略就會失效,具體如下:
@EnableEurekaClient
@SpringBootApplication
public class SpringcloudEurekaConsumerApplication {
/**
* 顯示實例化 負載均衡的策略對象,那麼默認的輪詢策略就會失效
* @return
*/
@Bean
public RandomRule createRule(){
return new RandomRule();
}
public static void main(String[] args) {
SpringApplication.run(SpringcloudEurekaConsumerApplication.class, args);
}
}
方式二:修改配置文件更換負載均衡策略
第二種方式在application.properties中設置分配的策略
#設置負載均衡策略 eureka-ribbon-provider 爲調用的服務的名稱
eureka-ribbon-provider.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
'eureka-ribbon-provider’是調用的服務的名稱,後面的組成部分是固定的。
同時註釋掉方式一中的內容