Spring Cloud 自定義Eureka Ribbon負載均衡策略

Java EE 目錄:https://blog.csdn.net/dkbnull/article/details/87932809
Spring Cloud 專欄:https://blog.csdn.net/dkbnull/column/info/36820
Spring Boot 專欄:https://blog.csdn.net/dkbnull/column/info/26341


在上一篇文章 Spring Cloud 服務註冊與發現 Eureka 中,我們使用Eureka的負載均衡策略解決了服務消費者在調用服務提供者接口時把提供者的地址硬編碼在消費者代碼裏的問題,同時實現了最簡單的負載均衡,接口會返回hello world,this is spring-boot-providerhello world,this is spring-boot-provider-v2。但是我們使用的是Eureka默認的負載均衡策略。
在微服務架構中,業務會被拆分成一個個彼此獨立的服務,服務間的通訊基於http restful。
Spring Cloud有兩種服務間調用的方式,RibbonFeign。下面我們簡單介紹下Ribbon。

0. 開發環境

  • IDE:IntelliJ IDEA 2017.1 x64

  • jdk:1.8.0_91

  • Spring Boot:2.0.9.RELEASE

  • Spring Cloud:Finchley.RELEASE

1. Ribbon簡介

Spring Cloud Ribbon 是一個基於HTTP和TCP的客戶端負載均衡工具,它基於Netflix Ribbon實現。通過Spring Cloud的封裝,可以讓我們輕鬆地將面向服務的REST模版請求自動轉換成客戶端負載均衡的服務調用。Spring Cloud Ribbon雖然只是一個工具類框架,但它不像服務註冊中心、配置中心、API網關那樣需要獨立部署。它幾乎存在於每一個Spring Cloud構建的微服務和基礎設施中。微服務間的調用、API網關的請求轉發等內容,實際上都是通過Ribbon來實現的。包括Spring Cloud的另一種服務調用方式Feign,也是基於Ribbon實現的。

2. Ribbon負載均衡策略

Ribbon的核心組件是IRule,IRule是所有負載均衡策略的父接口,其子類有:
在這裏插入圖片描述
每一個子類就是一種負載均衡策略

  • RandomRule:隨機選取負載均衡策略,隨機Random對象,在所有服務實例中隨機找一個服務的索引號,然後從上線的服務中獲取對應的服務。
  • RoundRobinRule:線性輪詢負載均衡策略。
  • WeightedResponseTimeRule:響應時間作爲選取權重的負載均衡策略,根據平均響應時間計算所有服務的權重,響應時間越短的服務權重越大,被選中的概率越高。剛啓動時,如果統計信息不足,則使用線性輪詢策略,等信息足夠時,再切換到WeightedResponseTimeRule。
  • RetryRule:使用線性輪詢策略獲取服務,如果獲取失敗則在指定時間內重試,重新獲取可用服務。
  • ClientConfigEnabledRoundRobinRule:默認通過線性輪詢策略選取服務。通過繼承該類,並且對choose方法進行重寫,可以實現更多的策略,繼承後保底使用RoundRobinRule策略。
  • BestAvailableRule:繼承自ClientConfigEnabledRoundRobinRule。從所有沒有斷開的服務中,選取到目前爲止請求數量最小的服務。
  • PredicateBasedRule:抽象類,提供一個choose方法的模板,通過調用AbstractServerPredicate實現類的過濾方法來過濾出目標的服務,再通過輪詢方法選出一個服務。
  • AvailabilityFilteringRule:按可用性進行過濾服務的負載均衡策略,會先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,還有併發的連接數超過閾值的服務,然後對剩餘的服務列表進行線性輪詢。
  • ZoneAvoidanceRule:本身沒有重寫choose方法,用的還是抽象父類PredicateBasedRule的choose。

2. 自定義負載均衡策略

上篇文章中我們使用Ribbon實現負載均衡,使用的是Ribbon默認的負載均衡策略,接下來我們自定義負載均衡策略。

2.1 代碼自定義策略

代碼自定義負載均衡策略需要注意,要避免Spring Boot的包掃描,自定義的規則必須在Eureka的規則實例化以後再實例化纔會生效。

2.1.1 新建負載均衡策略配置類

我們直接在上篇文章的spring-boot-consumer服務上進行修改

在啓動類SpringBootConsumerApplication的上層新建config包,然後新建LoadBalanced類。使用LoadBalanced類註冊一個新的IRule來替換Eureka。

package cn.wbnull.config;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.WeightedResponseTimeRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class LoadBalanced {

    @Bean
    public IRule iRule() {
        return new WeightedResponseTimeRule();
        //return new RandomRule();
    }
}

這裏我們想使用哪種策略就可以new哪種策略。

2.1.2 新建自定義負載均衡策略類

cn.wbnull.config包下新建loadbalancer包,再新建GlobalRule類,

package cn.wbnull.config.loadbalancer;

import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.Server;

import java.util.List;

public class GlobalRule implements IRule {

    private ILoadBalancer iLoadBalancer;

    @Override
    public Server choose(Object o) {
        List<Server> servers = iLoadBalancer.getAllServers();
        return servers.get(0);
    }

    @Override
    public void setLoadBalancer(ILoadBalancer iLoadBalancer) {
        this.iLoadBalancer = iLoadBalancer;
    }

    @Override
    public ILoadBalancer getLoadBalancer() {
        return this.iLoadBalancer;
    }
}

我們這裏自定義的負載均衡策略是始終取服務列表的第一個,其他自定義策略參考這種格式修改即可。

然後我們LoadBalanced類可以new一個GlobalRule

@Configuration
public class LoadBalanced {

    @Bean
    public IRule iRule() {
        return new GlobalRule();
    }
}

2.1.3 指定策略規則

SpringBootConsumerApplication類上新增註解 @RibbonClient(name = “spring-boot-provider”, configuration = cn.wbnull.config.LoadBalanced.class),表示spring-boot-provider服務使用cn.wbnull.config.LoadBalanced提供的負載均衡策略。

package cn.wbnull.springbootconsumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "spring-boot-provider", configuration = cn.wbnull.config.LoadBalanced.class)
public class SpringBootConsumerApplication {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringBootConsumerApplication.class, args);
    }
}

2.1.4 測試

我們依次啓動spring-cloud-eureka,spring-boot-provider,spring-boot-provider-v2,spring-boot-consumer。然後瀏覽器訪問http://127.0.0.1:8082/springbootconsumer/gateway,我們發現,無論怎麼刷新,返回信息都是同一個。
在這裏插入圖片描述
我們在GlobalRule中打上斷點,然後再刷新瀏覽器http://127.0.0.1:8082/springbootconsumer/gateway訪問頁面,可以看到每次刷新都能進到斷點這裏。
在這裏插入圖片描述
查看debug數據,servers.get(0)返回的是8081服務,我們瀏覽器接收到的返回參數也是8081服務的返回參數。
在這裏插入圖片描述

2.1.5 ComponentScan註解自定義掃描類

上面我們爲了避免Spring Boot的包掃描,將新建的負載均衡策略相關類都放在了啓動類SpringBootConsumerApplication的上級。我們也可以仍舊將負載均衡策略相關類放到啓動類SpringBootConsumerApplication同級或下級包內,然後使用 @ComponentScan 註解排除不需掃描的類。

  • 1、在cn.wbnull.springbootconsumer包下新建config包和config.loadbalancer;

  • 2、把2.1.1和2.1.2中新建的LoadBalanced類和GlobalRule類複製到cn.wbnull.springbootconsumer對應包下

  • 3、註釋掉2.1.1和2.1.2中新建的LoadBalanced類和GlobalRule類

  • 4、啓動類SpringBootConsumerApplication增加如下註解

@RibbonClient(name = "spring-boot-provider", configuration = cn.wbnull.springbootconsumer.config.LoadBalanced.class)
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = cn.wbnull.springbootconsumer.config.LoadBalanced.class)})

增加註解後SpringBootConsumerApplication:

package cn.wbnull.springbootconsumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "spring-boot-provider", configuration = cn.wbnull.springbootconsumer.config.LoadBalanced.class)
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = cn.wbnull.springbootconsumer.config.LoadBalanced.class)})
public class SpringBootConsumerApplication {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringBootConsumerApplication.class, args);
    }
}
  • 5、依次啓動spring-cloud-eureka,spring-boot-provider,spring-boot-provider-v2,spring-boot-consumer。然後瀏覽器訪問http://127.0.0.1:8082/springbootconsumer/gateway,效果跟剛纔相同。

2.2 配置文件自定義策略

上面我們使用代碼自定義策略,使用LoadBalanced類配置負載均衡,這種方式雖然能夠實現自定義負載均衡策略,但當我們有大量配置類時,對每個Ribbon客戶端的配置信息將會分散在這些配置類中,這樣管理起來極其麻煩。在Camden版本之後,Spring Cloud Ribbon對Ribbon客戶端的個性化配置進行了優化,可以通過服務id名稱.ribbon.參數=值的形式進行配置,如:

spring-boot-provider:
  ribbon:
    NFLoadBalancerRuleClassName: cn.wbnull.springbootconsumer.config.loadbalancer.GlobalRule

Ribbon配置的優先級:屬性配置 > Java配置 > Netflix Ribbon默認配置

2.2.1 修改啓動類

SpringBootConsumerApplication啓動類註釋掉如下代碼

@RibbonClient(name = "spring-boot-provider", configuration = cn.wbnull.springbootconsumer.config.LoadBalanced.class)
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = cn.wbnull.springbootconsumer.config.LoadBalanced.class)})

註釋後SpringBootConsumerApplication:

package cn.wbnull.springbootconsumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
public class SpringBootConsumerApplication {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringBootConsumerApplication.class, args);
    }
}

2.2.2 修改application.yml

application.yml增加如下配置

spring-boot-provider:
  ribbon:
    NFLoadBalancerRuleClassName: cn.wbnull.springbootconsumer.config.loadbalancer.GlobalRule

2.2.3 測試

依次啓動spring-cloud-eureka,spring-boot-provider,spring-boot-provider-v2,spring-boot-consumer。然後瀏覽器訪問http://127.0.0.1:8082/springbootconsumer/gateway,效果跟剛纔相同。

2.2.4 注意

當我們使用配置文件自定義負載均衡策略時,就不再需要2.1.1中新建的負載均衡策略配置類LoadBalanced,只需要2.1.2中新建的負載均衡策略類GlobalRule。



GitHub:https://github.com/dkbnull/SpringCloudDemo
微信:https://mp.weixin.qq.com/s/D3wHuZiuhqbqXI5MudwxMg
微博:https://weibo.com/ttarticle/p/show?id=2309404368874400313637
知乎:https://zhuanlan.zhihu.com/p/64760029



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