一、什麼是 Ribbon
目前主流的負載均衡方案有以下兩種:
- 集中式負載均衡,在消費者和服務提供方中間使用獨立的代理方式進行負載,有硬體的(比如 F5),也有軟體的(比如 Nginx)
- 客戶端根據自己的請求情況負載均衡,Ribbon 就是屬於客戶端自己做負載均衡。
Spring Cloud Ribbon 是基於 Netflix Ribbon 實現的一套客戶端的負載均衡器工具,Ribbon 客戶端元件提供了一系列的完善的配置,如超時,重試等,通過 Load Balancer 獲取到服務提供的所有機器例項,Ribbon 會自動基於某種規則(輪詢,隨機)去呼叫這些服務。Ribbon 也可以實現我們自己的負載均衡演算法。
1.1、客戶端的負載均衡
例如 Spring Cloud 中的 Ribbon,客戶端會有一個伺服器地址列表,在傳送請求前通過負載均衡演算法選擇一個伺服器,然後進行訪問,這是客戶端負載均衡;即在客戶端就進行負載均衡演算法分配。
1.2、服務端的負載均衡
例如 Nginx,通過 Nginx 進行負載均衡,先傳送請求,然後通過負載均衡演算法,在多個伺服器之間選擇一個進行訪問;即在伺服器端再進行負載均衡演算法分配
1.3、常見負載均衡演算法
- 隨機:通過隨機選擇伺服器進行執行,一般這種方式使用較少;
- 輪訓:負載均衡預設實現方式,請求來之後排隊處理‘
- 加權輪訓:通過對伺服器效能的分型,給高配置,低負載的伺服器分配更高的權重,均衡各個伺服器的壓力
- 地址 Hash:通過客戶端請求的 Hash 值取模對映進行伺服器排程,IP Hash
- 最小連結數:即使請求均衡了,壓力不一定會均衡,最小連線數法就是根據伺服器的情況,比如請求和積壓數等引數,將請求分配到當前壓力最小的伺服器上。
Nacos 使用 Ribbon
1、nacos-discovery 依賴了 ribbon,可以不再引入 ribbon依賴
2、新增 @LoadBalance
@Configuration
public class RestTemplateConfig {
// 可以直接 new RestTemplate(),但是官方建議使用構造器
@Bean
@LoadBalanced // 相當於 RestTemplate 有了負載均衡的呼叫機制
public RestTemplate restTemplate(RestTemplateBuilder builder) {
RestTemplate restTemplate = builder.build();
return restTemplate;
}
}
3、修改 controller
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/add")
public String add() {
System.out.println("下單成功!");
// restTemplate 呼叫
// String message = restTemplate.getForObject("http://127.0.0.1:8021/stock/reduce", String.class);
// 新增 @LoadBalance
String message = restTemplate.getForObject("http://stock-server/stock/reduce", String.class);
return "Hello World " + message;
}
三、Ribbon 負載均衡策略
3.1、負載均衡策略
IRule
這是所有負載均衡策略的父介面,裡邊的核心方法就是 choose 方法,用來選擇一個服務例項。
AbstractLoadBalancerRule
AbstractLoadBalancerRule 是一個抽象類,裡邊主要定義了一個 ILoadBalancer,就是我們上文所說的負載均衡器,負載均衡器的功能我們在上文已經說的很清楚了,這裡它的目的主要是輔助負責均衡負載策略選取合適的服務端例項
RandomRule
看名字就知道,這種負載均衡策略就是隨機選擇一個服務例項,看原始碼我們知道,在 RandomRule 的無參構造方法中初始化了一個 Random 物件,然後它重寫的 choose 方法又呼叫了 choose(ILoadBalancer Ib,Object key)這個過載的 choose 方法,在這個過載的 choose 方法中,每次利用 random 方法生成一個不再與服務例項總數的隨機數,並將該數作為下標以獲取一個服務例項
RoundRobinRule
RoundRobinRule 這種負載均衡策略叫做線性輪詢負載均衡策略。這個類的 choose(ILoadBanancer Ib,Object key)函式整體邏輯是這樣的:開啟一個計數器 count,在 while 迴圈中遍歷服務清單,獲取清單之前通過 incrementGetModulo 方法獲取一個下表,這個下標是一個不斷自增長的數先加1然後和服務清單總數取模之後獲取到的(所以這個下標從來不會越界),拿著下標再去服務清單列表中取服務,每次迴圈計數器都會加1,如果連續10次都沒有取到服務,則會報一個警告No available alive servers after 10 tries from load balancer: XXXX。
RertyRule(在輪詢的基礎上進行重試)
看名字就知道這種負載均衡策略帶有重試功能。首先 RetryRule 中又定義了一個 subRule,它的實現類是 RoundRobinRule,然後在 RetryRule 的choose(ILoadBalancer Ib, Object key)方法中,每次還是採用RoundRobinRule中的choose規則來選擇一個服務例項, 如果選到的例項正常就返回,如果選擇的服務例項為null或者已經失效,則在失效時間 deadline 之 前不斷的進行重試(重試時獲取服務的策略還是RoundRobinRule中定義的策略),如果超過 了deadline還是沒取到則會返回一個null。
WeightedResponseTimeRule(權重-nacos 的 NacosRule,Nacos 還擴充了一個自己的基於配置的權重擴充套件)
WeightedResponseTimeRule是RoundRobinRule的一個子類,在WeightedResponseTimeRule中對RoundRobinRule的功能進行了擴充套件,WeightedResponseTimeRule中會根據每一個例項的執行情況來給計算出該例項的一一個權重,然後在挑選例項的時候則根據權重進行挑選,這樣能夠實現更優的例項呼叫。WeightedResponseTimeRule中有一 個名叫 DynamicServerWeightTask 的定時任務,預設情況下每隔30秒會計算一次各個服務例項的權重, 權重的計算規則也很簡單,如果一個服務的平均響應時間越短則權重越大,那麼該服務例項被選中執行任務的概率也就越大。
ClientConfigEnabledRoundRobinRule
ClientConfigEnabledRoundRobinRule選擇策略的實現很簡單,內部定義了RoundRobinRule, choose方法還是採用了RoundRobinRule的choose方法,所以它的選擇策略和RoundRobinRule的選擇策略一致,不贅述。
BestAvailableRule
BestAvailableRule繼承自ClientConfigEnabledRoundRobinRule,它在ClientConfigEnabledRoundRobinRule的基礎 上主要增加了根據loadBalancerStats中儲存的服務例項的狀態資訊來過濾掉失效的服務例項的功能,然後順便找出併發請求最小的服務例項來使用。然而loadBalancerStats有可能為null, 如果loadBalancerStats為null, 則BestAvailableRule將採用它的父類即ClientConfigEnabledRoundRobinRule的服務選取策略(線性輪詢)
ZoneAvoidanceRule(預設規則,符合判斷 server 所在區域的效能和 server 的可用性選擇伺服器)
ZoneAvoidanceRule是PredicateBasedRule的一個實現類, 只不過這裡多一個過濾條件, ZoneAvoidanceRule中的過濾條件是以ZoneAvoidancePredicate為主過濾條件和以AvailabilityPredicate為次過濾條件組成的一個叫做CompositePredicate的組合過濾條件, 過濾成功之後,繼續採用線性輪詢(RoundRobinRule)的方式從過濾結果中選擇一個出來。
AvaliabilityFilteringRule(先過濾掉故障例項,再選擇併發較小的例項)
過濾掉一直連線失敗的被標記為circuit tripped的後端Server,並過濾掉那些高併發的後端Server或者使用一個AvailbilityPredicate來包含過濾server的邏輯, 其實就是檢查status裡,記錄的各個Server的執行狀態。
3.2、修改預設的負載均衡策略
3.2.1、通過修改配置類的方式修改負載均衡策略
1、配置類:
注意這個配置類不能被 @SpringBootApplication 所在的包下,不能被其掃描到,不然配置全域性共享
@Configuration
public class RibbonRandomRuleConfig {
/**
* 方法名一定要叫 iRule
* @return IRule
*/
@Bean
public IRule iRule() {
// 配置隨機負載均衡策略
return new RandomRule();
}
}
2、在springboot入口類中新增註解
這裡的 stock-server 就是 Controller 裡呼叫的介面名稱
@SpringBootApplication
@RibbonClients(value = {
@RibbonClient(name = "stock-server",configuration = RibbonRandomRuleConfig.class)
})
public class OrderRibbonServerApplication {
public static void main(String[] args) {
SpringApplication.run(OrderRibbonServerApplication.class,args);
}
}
yml 配置如下
server:
port: 8030
# 應用名稱(nacos 會自動將該名稱當作服務名稱)
spring:
application:
name: order-server
cloud:
nacos:
server-addr: 127.0.0.1
discovery:
username: xxxxxx
password: xxxxxx
# 命名環境用來隔離服務
namespace: public
3、啟動服務
將修改負載均衡策略為隨機的服務啟動,埠為 8030,啟動 stock-nacos 服務,埠為 8081 和 8082
4、請求測試
位址列輸入:http://localhost:8030/order/add
發現請求確到的服務確實是隨機的。
3.2.2 通過修改配置的方式
1、修改yml檔案
# 被呼叫的微服務名稱
stock-server:
ribbon:
# 指定使用 Nacos 提供的負載均衡策略(優先同一叢集的例項,機遇隨機&權重)
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
2、修改服務的權重
3、測試生效
3.2.3、自定義負載均衡策略
通過實現 IRule 介面可以自定義負載均衡策略,主要的選擇服務邏輯在 choose 方法中。
1) 實現基於 Nacos 權重的負載均衡策略
本作品採用《CC 協議》,轉載必須註明作者和本文連結