Ribbon 簡介 Ribbon 是 Netfix 客戶端的負載均衡器,可對 HTTP 和 TCP 客戶端的行為進行控制。為 Ribbon 配置服務提供者地址後,Ribbon 就可以基於某種負載均衡演算法自動幫助服務消費者去請求。Ribbon 預設提供了很多負載均衡演算法,例如輪詢、隨機等,也可以為 R ...
Ribbon 簡介
Ribbon 是 Netfix 客戶端的負載均衡器,可對 HTTP 和 TCP 客戶端的行為進行控制。為 Ribbon 配置服務提供者地址後,Ribbon 就可以基於某種負載均衡演算法自動幫助服務消費者去請求。Ribbon 預設提供了很多負載均衡演算法,例如輪詢、隨機等,也可以為 Ribbon 實現自定義的負載均衡演算法
Ribbon 有以下幾個重要概念:
- Rule:該組件主要決定從候選伺服器中返回哪個伺服器地址進行遠程調用的操作
- Ping:在後臺運行的組件,用來確認哪些伺服器是存活可用的
- ServerList:當前可以用作 LB 的伺服器列表,該列表可以是靜態的,也可以是動態的。如果是動態列表(例如從 Eurka 伺服器獲取),就會有一個後臺線程按照時間間隔刷新列表
Ribbon 提供了以下幾種 Rule:
- RoundRobinRule:最簡單的規則,會在 ServerList 中依次輪詢調用
- RandomRule:隨機
- AvailabilityFileringRule:在這種規則下 Ribbon 集成了 Hystrix 的功能,預設情況下調用某個遠程方法失敗三次後斷路器的開關會被打開,而之後的請求中 Ribbon 會跳過這個伺服器地址,直到三十秒之後斷路器關閉後才會重新加入調用列表
- WeightedResponseTimeRule:將響應時間作為權重的負載規則,某個伺服器的響應時越長,它的權重就越低,具體選擇伺服器時,結合權重進行隨機選擇
- RetryRule:按照 RoundRobinRule(輪詢)策略獲取服務,如果獲取服務失敗,就在指定時間內重試,獲取可用的服務
- BestAvailableRule:先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,然後選擇一個併發量最小的服務
- ZoneAvoidanceRule:複合判斷 Server 所在區域的性能和 Server 的可用性選擇伺服器
負載均衡演算法
服務消費者從服務配置中心獲取服務的地址列表後需要選取其中一臺發起 RPC/HTTP 調用,這時需要用到具體的負載均衡演算法
1. 輪詢法
輪詢法是指將請求按順序輪流分配到後端伺服器上,均衡地對待後端的每一臺伺服器,不關心伺服器實際的連接數和當前系統負載
2. 加權輪詢法
簡單的輪詢法並不考慮後端機器的性能和負載差異,加權輪詢法可以很好地處理這一問題,它將按照順序且按照權重分派給後端伺服器,給性能高、負載低的機器配置較高的權重,讓其處理較多的請求,給性能低、負載高的機器配置較低的權重,讓其處理較少的請求
假設有 9 個客戶端請求、3 台後端伺服器,後端伺服器 1 被賦予權值 1,後端伺服器2被賦予值 2,後端伺服器 3 賦值 3,這樣一來,客戶端請求 1、2、3 都被分派到伺服器 3 處理,客戶端請求 4、5 被分派到伺服器 2 處,客戶端請求 6 被分派到伺服器 1 處理,客戶端請求 7、8、9 被分派到伺服器 3 處理,以此類推
3. 隨機法
隨機法也很簡單,就是隨機選擇一臺後端伺服器進行請求處理,由於每次伺服器被挑中的概率都一樣,因此客戶端的請求可以被均勻地分派到所有的後端伺服器上
4. 加權隨機法
加權隨機法跟加權輪詢法類似,根據後臺伺服器不同的配置和負載情況配置不同的權重,不同的是,它是按照權重來隨機選取伺服器的,而非順序
比如希望抽到 A 的概率是 50%、抽到 B 和 C 的概率是 20%、抽到 D 的概率是 10%,一般來說,我們可以給各項附加一個權重,抽取的概率正比於這個權重,上述集合就成了 {A:5,B:2,C:2,D:1),擴展這個集合,使每一項出現的次數與其權重正相關,即 {A,A,A,A,A,B,BC,C,D},然後就可以用均勻隨機演算法從中選取了
5. 源地址哈希法
源地址哈希是根據獲取客戶端的 IP 地址,通過哈希函數計算得到一個數值,用該數值對伺服器列表的大小進行取模運算,得到的結果便是客戶端要訪問伺服器的序號。採用源地址哈希法進行負載均衡,當後端伺服器列表不變時,同一個 IP 地址的客戶端,每次都會映射到同一臺後端伺服器進行訪問,但當後端伺服器增加或者減少時,由於次數用於取模的伺服器總數發生了變化,就導致同一哈希值的請求無法命中同一臺伺服器,節點數越高,命中率越低
6. 一致性哈希法
一致性哈希法解決了分散式環境下機器增加或者減少時簡單的取模運算無法獲取較高命中率的問題,通過一個一致性哈希環的數據結構實現映射,具體演算法過程為:先構造一個長度為 2 的 32 次方的整數環(一致性哈希環),根據節點計算得出的哈希值將緩存伺服器節點放置在這個哈希環上,然後在哈希環上順時針查找距離這個哈希值最近的伺服器節點,完成請求到伺服器的映射
假設現在增加一臺伺服器 4,那麼影響的就只有一個的請求,也就是說原本到伺服器 1 的請求會被映射到伺服器 4 上,雖然也會影響到整個集群,但是影響的只是加粗的那一段而已,這種影響要小得多。更重要的是,集群中緩存伺服器節點越多,增加節點帶來的影響越小
第一個 Ribbon 程式
創建名為 ribbon-provider 的項目,添加配置文件 application-01.properties 和 application-02.properties
# application-01.properties 配置文件內容
server.port=8080
# application-02.properties 配置文件內容
server.port=8081
開發 UserController 類
@RestController
@RequestMapping("user")
public class UserCon {
@Resource
private Environment environment;
public String getPort() {
return environment.getProperty("local.server.port");
}
@RequestMapping("getName")
public String getUserName (){
return "hello,ay" + "-" + getPort();
}
}
通過使用不同的配置文件,可以啟動多個 SpringBoot 應用,分別啟動 ribbon-provider-8080 和 ribbon-provider-8081
創建名為 ribbon-consumer 的項目,pom.xml 添加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
添加配置文件 application.yml
my-client: #負載均衡配置
ribbon:
listOfServers: localhost:8080,localhost:8081 # 配置服務列表
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule # 配置負載均衡演算法 RoundRobinRule(輪詢)
Ribbon 的配置格式是 <clientName>:ribbon:需要配置的屬性
,<clientName>
是 Ribbon 的客戶端名稱,如果省略就配置所有客戶端,配置的屬性有以下幾種:
- NFLoadBalancerClassName:配置 ILoadBalancer 的實現類
- NFLoadBalancerRuleClassName:配置 IRule 的實現類
- NFLoadBalancerPingClassName:配置 IPing 的實現類
- NIWSServerListClassName:配置 ServerList 的實現類
- NIWSServerListFilterClassName:配置 ServerListFilter 的實現類
在 main 方法中添加如下代碼:
@SpringBootApplication
public class RibbonConsumerApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(RibbonConsumerApplication.class, args);
//獲取客戶端
RestClient client = (RestClient) ClientFactory.getNamedClient("my-client");
//調用UserController類的getUserName 方法
HttpRequest request = HttpRequest.newBuilder().uri("/user/getName").build();
//迴圈調用
for(int i = 0; i<10; i++) {
HttpResponse response = client.executeWithLoadBalancer(request);
String result = response.getEntity(String.class);
System.out.println(result);
}
}
}
啟動 ribbon-consumer 項目,從列印信息中可以看出,服務通過輪詢的方式調用
Ribbon 整合 Nacos & 自定義負載均衡策略
創建 ribbon-custom-consumer 的項目,添加配置類
@Configuration
public class RibbonConfig {
@Bean
public IRule ribbonRule() {
// ribbon預設使用的是zoneAvoidanceRule規則,這裡修改為自定義方式
return new MyRule();
}
}
創建 MyRule 類,用來自定義負載均衡規則
/**
* 負載規則:始終返回第一個服務
*/
public class MyRule extends AbstractLoadBalancerRule {
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object o) {
ILoadBalancer loadBalancer = getLoadBalancer();
// 獲取所有的服務
List<Server> servers = loadBalancer.getAllServers();
// 始終返回第一個服務
return servers.get(0);
}
}
自定義指定 Ribbon 客戶端的配置
/**
* 使用 RibbonClient 為特定 name 的 Ribbon Client 自定義配置
* 使用 @RibbonClient 的 configuration 屬性指定 Ribbon 的配置類
*/
@Configuration
@RibbonClient(name = "service-provider", configuration = RibbonConfig.class)
public class TestConfig {
}
在 application.properties 配置文件中添加如下配置
server.port=7089
spring.application.name=service-custom
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
在啟動類中添加 @EnableDiscoveryClient
註解
@EnableDiscoveryClient
@SpringBootApplication
public class RibbonCustomApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonCustomApplication.class, args);
}
}
創建 TesController類,具體代碼如下
@RestController
@RequestMapping("test")
public class TestController {
@Resource
private LoadBalancerClient loadBalancerClient;
@RequestMapping("getUserName")
public String getUserName() {
for (int i = 0; i < 20; i++) {
//獲取service-provider服務
ServiceInstance serviceInstance = loadBalancerClient.choose("service-provider");
//列印當前選擇的是哪個節點
System.out.println(serviceInstance.getServiceId() + serviceInstance.getHost() + "; " + serviceInstance.getPort());
}
return "hello,ay";
}
}
在上述步驟中,我們創建了 ribbon-custom-consumer 項目,並定義了負載均衡規則 MyRule,服務啟動後註冊到 Nacos 中,這樣一來,在調用 getUserName 方法時,會從 Nacos 中獲取已註冊的服務提供者列表,並按照我們自定義的負載規則進行調用