微服務架構(gòu) | 4.1 基于 Ribbon 的負載均衡詳解

前言

參考資料
《Spring Microservices in Action》
《Spring Cloud Alibaba 微服務原理與實戰(zhàn)》
《B站 尚硅谷 SpringCloud 框架開發(fā)教程 周陽》

Spring Cloud Ribbon 是基于 Netflix Ribbon 實現(xiàn)的一套客戶端負載均衡的工具走贪;提供客戶端的軟件負載均衡算法和服務調(diào)用宙帝;


1. Ribbon 基礎(chǔ)知識

1.1 Ribbon 是什么

  • Spring Cloud Ribbon 是基于 Netflix Ribbon 實現(xiàn)的一套客戶端負載均衡的工具赁酝;提供客戶端的軟件負載均衡算法和服務調(diào)用问欠;
  • Ribbon 客戶端組件提供一系列完善的配置項如連接超時矩屁,重試等烛愧。簡單的說,就是在配置文件中列出 Load Balancer(簡稱LB)后面所有的機器续崖,Ribbon 會自動基于某種規(guī)則(如簡單輪詢另伍,隨機連接等)去連接這些機器修然;
  • 可以很容易使用Ribbon實現(xiàn)自定義的負載均衡算法;

1.2 與 Ribbon 交互的三種級別層次

  • Spring DiscoveryClient:提供了對 Ribbon 和 Ribbon 中緩存的注冊服務的最低層次訪問质况;
  • 啟用了 RestTemplate 的 Spring DiscoveryClient愕宋;
  • Netflix Feign 客戶端

1.3 Ribbon在工作時分成兩步

Ribbon 架構(gòu)圖
  • 第一步先選擇 EurekaServer结榄,它優(yōu)先選擇在同一個區(qū)域內(nèi)負載較少的 server中贝;
    第二步再根據(jù)用戶指定的策略,在從 server 取到的服務注冊列表中選擇一個地址臼朗;

1.4 服務的提供者與消費者

  • 提供者:服務提供者將自己注冊進注冊中心邻寿,讓消費者發(fā)現(xiàn);在本例中有多個提供者給消費者提供服務视哑;
  • 消費者:消費者使用服務發(fā)現(xiàn)绣否,找到提供者并調(diào)用提供者服務;在本例中只有一個消費者在多個提供者中選出一個為自己服務挡毅;

1.5 Ribbon 核心組件 IRule

IRule 負載均衡
  • 根據(jù)特定算法中從服務列表中選取一個要訪問的服務蒜撮;
  • 定義了負載均衡的方式;
  • 有以下幾種負載均衡的實現(xiàn)方式:
    • RoundRobinRule:輪詢跪呈;
    • RandomRule:隨機段磨;
    • RetryRule:先按照 RoundRobinRule 的策略獲取服務,如果獲取服務失敗則在指定時間內(nèi)會進行重試耗绿,獲取可用的服務苹支;
    • WeightedResponseTimeRule:對 RoundRobinRule 的擴展,響應速度越快的實例選擇權(quán)重越大误阻,越容易被選擇债蜜;
    • BestAvailableRule:會先過濾掉由于多次訪問故障而處于斷路器跳閘狀態(tài)的服務,然后選擇一個并發(fā)量最小的服務究反;
    • AvailabilityFilteringRule:先過濾掉故障實例寻定,再選擇并發(fā)較小的實例;
    • ZoneAvoidanceRule:默認規(guī)則奴紧,復合判斷 server 所在區(qū)域的性能和 server 的可用性選擇服務器特姐;


2. 服務消費者獲取提供者的三個層次示例

2.1 引入 pom.xml 依賴

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
  • 如果使用 Eureka 作為注冊中心,則不用引入該依賴黍氮,因為 Eureka 的依賴里包含 ribbon 相關(guān)依賴 jar 包唐含;

2.2 使用 Spring DiscoveryClient 查找服務實例

2.2.1 在主程序類上添加注解

  • @EnableDiscoveryClient:表明可以被注冊中心發(fā)現(xiàn)。是 Sring Cloud 的觸發(fā)器沫浆,其作用是使應用程序能夠使用 DiscoveryClient 和 Ribbon 庫捷枯;

2.2.2 使用 DiscoveryClient 查找信息

在服務消費者的 client 包下;

@Component
public class ProviderDiscoveryClient {

    //自動注入 DiscoveryClient 類专执,該類用于與 Ribbon 交互
    @Autowired
    private DiscoveryClient discoveryClient;

    public Provide getProvide(String providerId) {
        RestTemplate restTemplate = new RestTemplate();
        //獲取服務提供者的所有實例列表淮捆,ServiceInstance 用于保存關(guān)于服務的特定實例(包括主機名、端口荷 URL)
        List<ServiceInstance> instances = discoveryClient.getInstances("provider-instance-name");

        if (instances.size()==0) return null;
        //檢索要調(diào)用的服務端點
        String serviceUri = String.format("%s/providers/%s",instances.get(0).getUri().toString(), providerId);

        //使用標準的 Spring REST 模板類去調(diào)用服務
        ResponseEntity< provider > restExchange =
                restTemplate.exchange(
                        serviceUri,
                        HttpMethod.GET,
                        null, Provider.class, providerId);
        
        return restExchange.getBody();
    }
}
  • 這種方法存在以下問題:

    • 沒有利用 Ribbon 的客戶端負載均衡,調(diào)用哪個服務實例需要開發(fā)人員定義攀痊;
    • 開發(fā)人員必須構(gòu)建一個用來調(diào)用服務的 URL桐腌;
    • 實例化 ResTemplate 類,不符合 Spring IoC 規(guī)范苟径;
  • 結(jié)合本篇《5. 本地負載均衡器的實現(xiàn)(消費者)》即可用到客戶端負載均衡案站,即:開發(fā)人員定義了本地負載均衡器來實現(xiàn)了負載均衡;

2.3 使用帶有 Ribbon 功能的 Spring RestTemplate 調(diào)用服務

2.3.1 在主程序類上添加注解

@SpringBootApplication //只需要這個注解即可
public class Application {
  @LoadBalanced //告訴 Spring Cloud 創(chuàng)建一個支持 Ribbon 的 RestTemplate
  @Bean
  public RestTemplate getRestTemplate(){
      return new RestTemplate();
  }
  public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
  }
}
  • Spring Cloud 早期版本中棘街,RestTemplate 類默認自動支持 Ribbon蟆盐;
  • 自從 Spring Cloud 發(fā)布 Angel 版本后,Spring Cloud 中的 RestTemplate 不再支持 Ribbon遭殉;
  • 因此石挂,后續(xù)版本必須使用 @LoadBalanced 注解顯式標注,才能將 Ribbon 和 RestTemplate 一起使用险污;
  • *RestTemplate 不一定放在主程序類里痹愚;也可以在 config 包下新建一個 ApplicationContextConfig 配置類,將 RestTemplate 放在該類里:
@Configuration
public class ApplicationContextConfig{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

2.3.2 使用 Ribbon 的 RestTemplate 來調(diào)用服務

在服務消費者的 client 包下罗心;

@Component
public class ProviderRestTemplateClient {
    //自動注入即可里伯,不用實例化
    @Autowired
    RestTemplate restTemplate;

    public Provider getProvider(String providerId){
        ResponseEntity<Provider> restExchange =
                restTemplate.exchange(
                        //使用 Eureka 服務 ID 來構(gòu)建目標 URL
                        "http://provider-instance-name/providers/{providerId}",
                        HttpMethod.GET,
                        null, Provider.class, providerId);
        return restExchange.getBody();
    }
}
  • 通過使用 RestTemplate 類,Ribbon 將在所有服務實例之間輪詢負載均衡所有請求渤闷;

2.4 使用 Netflix Feign 客戶端調(diào)用服務

Feign 相關(guān)知識將在下篇《4.2 基于 Feign 與 OpenFeign 的服務接口調(diào)用》中說明疾瓮,這里僅把重點放在與上述兩種調(diào)用提供者服務的區(qū)別與對比;

2.4.1 在主程序類上添加注解

@EnableFeignClients:表示啟用 Feign 客戶端飒箭;

2.4.2 定義用于調(diào)用服務提供者的 Feign 接口

@FeignClient("provider-instance-name") //標識服務為 feign 的客戶端
public interface ProviderFeignClient {
    //定義端點的路徑和動作
    @RequestMapping( 
            method= RequestMethod.GET,
            value="/providers/{providerId}",
            consumes="application/json")
    //定義傳入端點的參數(shù)狼电,該方法可以由客戶端調(diào)用以觸發(fā)組織服務        
    Provider getProvider(@PathVariable("providerId") String providerId);
}
  • 要是用 ProviderFeignClient 類,開發(fā)人員只需要自動裝配并使用它即可弦蹂;


3. 通過 java 配置類自定義負載均衡算法示例(消費者服務)

指切換默認的負載均衡算法肩碟,切換后的仍為現(xiàn)成的(與本地負載均衡器有所區(qū)別,本地負載均衡器要自己實現(xiàn))凸椿;

3.1 編寫配置類

  • 注意:自定義配置類不能放在 @ComponentScan 所掃描的當前包下以及子包下削祈,否則自定義的配置類會被所有的Ribbon客戶端所共享,達不到自定義的目的脑漫;
  • @ComponentScan 注解被封裝到主啟動類上的 @SpringBootApplication 注解髓抑。其默認掃描主啟動類所在包及其子包,因此我們要返回上一級目錄新建一個 myRule 目錄存放我們自定義的負載均衡配置類优幸;
自定義負載均衡算法的文件目錄結(jié)構(gòu)
@Configuration
public class MySelfRule {
    @Bean
    public IRule myRule(){
        return new RandomRule();//定義為隨機
    }
}

3.2 主啟動類上添加注解

  • @RibbonClient(name = "provider-instance-name" ,configuration=MySelfRule.class):表示使用自定義負載均衡算法吨拍;
    • name:指定服務提供者的實例名稱;
    • configuration:指定需要使用哪個配置類的負載均衡网杆;
    • 表示 provider 服務使用 MySelfRule 對應的 Ribbon 配置羹饰;
  • 同樣伊滋,需要對 RestTemplate 類用 @LoadBalanced 注解顯示聲明;


4. 通過配置自定義負載均衡算法示例(消費者服務)

指切換默認的負載均衡算法队秩,切換后的仍為現(xiàn)成的(與本地負載均衡器有所區(qū)別笑旺,本地負載均衡器要自己實現(xiàn));

4.1 修改 bootstrap.yml 配置文件

  • 上述 java 配置類的效果等價于下面這樣的配置文件:
#服務提供者的實例名稱
provider-instance-name:
  ribbon:
    #代表 Ribbon 使用的負載均衡策略刹碾,屬性的值為:IRule 的實現(xiàn)類
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    
    #其他可用的配置屬性
    # NFLoadBalancerClassName : 配置 ILoadBalancer 的實現(xiàn)類
    # NFLoadBalancerPingClassName : 配置 IPing 的實現(xiàn)類
    # NIWSServerListClassName: 配置 ServerList 的實現(xiàn)類
    # NIWSServerListFilterClassName: 配置 ServerListtFilter 的實現(xiàn)類

4.2 主程序類

  • 不需要 @RibbonClient 注解燥撞;
  • 同樣,需要對 RestTemplate 類用 @LoadBalanced 注解顯示聲明迷帜;


5. 本地負載均衡器的實現(xiàn)(消費者)

本地負載均衡器不同于自定義負載均衡算法;前者的負載均衡算法需要自己手動實現(xiàn)色洞,后者只是切換成另一種現(xiàn)成的負載均衡算法戏锹;

5.1 不使用 RestTemplate

  • 即主程序類不需要對 RestTemplate 類用 @LoadBalanced 注解顯示聲明;
  • 可以刪去也可以注釋 @LoadBalanced 注解火诸;

5.2 定義負載均衡接口

可以新建一個包锦针,專門存放我們自己寫的負載均衡算法;

public interface LoadBalancer{
    ServiceInstance instances(List<ServiceInstance> serviceInstances);
}

5.3 實現(xiàn)負載均衡接口

  • 可以根據(jù)業(yè)務要求寫不同的負載均衡算法置蜀,這里僅提供一種示例奈搜;
  • 該示例實現(xiàn)了一種較為簡單的原子性的負載均衡算法;
@Component
public class MyLB implements LoadBalancer{
    private AtomicInteger atomicInteger = new AtomicInteger(0);
    public final int getAndIncrement(){
        int current;
        int next;

        do {
            current = this.atomicInteger.get();
            next = current >= 2147483647 ? 0 : current + 1;
        }while(!this.atomicInteger.compareAndSet(current,next));
        System.out.println("*****第幾次訪問盯荤,次數(shù)next: "+next);
        return next;
    }

    //負載均衡算法:rest接口第幾次請求數(shù) % 服務器集群總數(shù)量 = 實際調(diào)用服務器位置下標  馋吗,每次服務重啟動后rest接口計數(shù)從1開始。
    @Override
    public ServiceInstance instances(List<ServiceInstance> serviceInstances){
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}

5.4 在 controller 接口中使用本地負載均衡器

  • 類似于本篇《2.2 使用 Spring DiscoveryClient 查找服務實例》秋秤;
  • 不同之處在于 2.2 沒有負載均衡功能宏粤,這里在 2.2 的基礎(chǔ)上,開發(fā)人員自己定義了本地負載均衡器灼卢,不使用 Ribbon 提供的負載均衡绍哎,故《5.1 不使用 RestTemplate》中提到的不用對 RestTemplate 類使用 @LoadBalanced 注解顯示聲明
@RestController
public class OrderController{
    //服務提供者示例的名字
    public static final String PAYMENT_URL = "http://provider-instance-name";
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private LoadBalancer loadBalancer;
    @Resource
    private DiscoveryClient discoveryClient;
 
    @GetMapping(value = "/provider/mylb")
    public String getProviderLB(){
        //獲取服務提供者的所有實例列表
        List<ServiceInstance> instances = discoveryClient.getInstances("provider-instance-name");
        if(instances == null || instances.size() <= 0){
            return null;
        }
        //使用本地負載均衡器選出提供者服務
        ServiceInstance serviceInstance = loadBalancer.instances(instances);
        URI uri = serviceInstance.getUri();
        return restTemplate.getForObject(uri+"/provider/mylb",String.class);
    }
}



最后

\color{blue}{\rm\small{新人制作鞋真,如有錯誤崇堰,歡迎指出,感激不盡涩咖!}}

\color{blue}{\rm\small{歡迎關(guān)注我海诲,并與我交流!}}

\color{blue}{\rm\small{如需轉(zhuǎn)載抠藕,請標注出處饿肺!}}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市盾似,隨后出現(xiàn)的幾起案子敬辣,更是在濱河造成了極大的恐慌雪标,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件溉跃,死亡現(xiàn)場離奇詭異村刨,居然都是意外死亡,警方通過查閱死者的電腦和手機撰茎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門嵌牺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人龄糊,你說我怎么就攤上這事逆粹。” “怎么了炫惩?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵摇天,是天一觀的道長忽匈。 經(jīng)常有香客問我补君,道長甥郑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任筋蓖,我火速辦了婚禮卸耘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘粘咖。我一直安慰自己蚣抗,他們只是感情好,可當我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布涂炎。 她就那樣靜靜地躺著忠聚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪唱捣。 梳的紋絲不亂的頭發(fā)上两蟀,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天,我揣著相機與錄音震缭,去河邊找鬼赂毯。 笑死,一個胖子當著我的面吹牛拣宰,可吹牛的內(nèi)容都是我干的党涕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼巡社,長吁一口氣:“原來是場噩夢啊……” “哼膛堤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起晌该,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤肥荔,失蹤者是張志新(化名)和其女友劉穎绿渣,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體燕耿,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡中符,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了誉帅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淀散。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蚜锨,靈堂內(nèi)的尸體忽然破棺而出档插,到底是詐尸還是另有隱情,我是刑警寧澤踏志,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布阀捅,位于F島的核電站,受9級特大地震影響针余,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凄诞,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一圆雁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧帆谍,春花似錦伪朽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至窖剑,卻和暖如春坚洽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背西土。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工讶舰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人需了。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓跳昼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肋乍。 傳聞我的和親對象是個殘疾皇子鹅颊,可洞房花燭夜當晚...
    茶點故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內(nèi)容