玩轉SpringCloud專題(八)-SpringCloud之Ribbon負載均衡

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’是調用的服務的名稱,後面的組成部分是固定的。
同時註釋掉方式一中的內容

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章