學習 spring-cloud-aibaba第三篇,負載均衡Ribbon


特別聲明:文章的內容學習自慕課網大目老師的微服務視頻,視頻鏈接:https://coding.imooc.com/learn/list/358.html,大目老師還寫了很多微服務有關的手記,都很實用!

前情提要:上一篇博文已經介紹了服務註冊nacos的實現。現在我註冊了content-centeruser-center兩個服務,content-center使用RestTemplate調用user-center服務,使用ribbon做負載均衡

1.ribbon的組成

在這裏插入圖片描述

  • IRule 核心接口,負載均衡的規則,默認是ZoneAvoidanceRule
  • ServerListUpdater,更新ServerList的策略,當nacos上server實例個數發生變化時,ribbon裏的ServerList什麼時候去更新呢?默認值PollingServerListerUpdate定時更新

2.項目使用Ribbon

2.1 引入依賴

Ribbon已經包含在spring-cloud-starter-alibaba-nacos-discovery裏了,所以不用再重複添加依賴了
在這裏插入圖片描述

2.2 加註解

啓動類那邊不用加註解,註解加載RestTemplate上面@LoadBalanced

@Configuration
public class RestTemplateConfig {

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

2.3 寫配置

暫時不用寫配置

3.負載均衡規則 IRule

在這裏插入圖片描述

3.1 測試 ZoneAvoidanceRule 默認規則

這裏的zone是什麼,我也不知道,我看源碼是個String。我測試的環境應該是沒有zone的,看啓動當然日誌是 UNKNOWN ,所以它相當於RoundRobinRule輪詢。
在這裏插入圖片描述
啓動三個user-center和一個content-center
在這裏插入圖片描述

  • content-center 代碼:
@RestController
@RequestMapping("poem")
public class PoemController {

    private static final Logger log = LoggerFactory.getLogger(PoemController.class);

    @Autowired
    private PoemService poemService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping(value = "testRibbon")
    public Object testRibbon() {
//        List<ServiceInstance> instances = discoveryClient.getInstances("user-center");
//        String targetUrl = instances.stream()
//                .map(instance -> instance.getUri().toString() + "/reciteHis/testAno")
//                .findFirst()
//                .orElseThrow(() -> new IllegalArgumentException("當前沒有實例!"));
        String targetUrl= "http://user-center/reciteHis/testAno";
        log.info("targetUrl={}",targetUrl);
        Page forObject = restTemplate.getForObject(targetUrl, Page.class);
        return ResponseVO.success(forObject);
    }

String targetUrl= “http://user-center/reciteHis/testAno 裏的user-center就是nacosuser-center的服務名

  • user-center 代碼;
@RestController
@RequestMapping("/reciteHis")
public class ReciteHisController {

    private static final Logger log = LoggerFactory.getLogger(ReciteHisController.class);

    @Autowired
    private ReciteHisService reciteHisService;

    @Autowired
    private MemberService memberService;

    @GetMapping(value = "testAno")
    public Object memberRctHisAno(HttpServletRequest request){
        Page<ReciteHisOT> page = new Page<>(1, 2);
        page.setRecords(reciteHisService.selectUserListPageAno(page,"3"));
        log.info("我是{},我被調用了",request.getRequestURL());
        return page;
    }

隨便提供一個測試接口就行,返回的內容不重要

  • 訪問content-center的 http://localhost:8081/poem/testRibbon 接口測試,刷新6次
    看user-center的日誌,三個console窗口各顯示被調用了2次,是平均的,看來是輪詢規則沒錯
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述

3.2 測試 RandomRule 隨機規則

  • 寫配置
    application.yml。沒有代碼提示,純手敲,user-center代表針對的是user-center服務寫的配置 (這裏用的是屬性配置方式,還有一種java代碼的配置方式)
user-center:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
  • 測試
    請求content-center的 http://localhost:8081/poem/testRibbon 接口測試,請求10次
    user-center服務,我關掉了一個,三個太多了,現在2個服務實例,一個被調用6次,一個4次,簡單證明是隨機
    在這裏插入圖片描述
    在這裏插入圖片描述

3.3 其它的內置規則沒那麼方便測試,我不測試了

3.4 ribbon的全局配置

上面提到了ribbon的配置有兩種方式

  • 屬性配置
    這種方式暫時無法實現全局配置,剛纔配置的RandomRule也只能對user-center服務起作用,如果調用其它服務,則還是默認的ZoneAvoidanceRule
  • java代碼配置
    step1:新建一個和com包平行的configuration包,新建RibbonConfiguration類
    新建一個平行的包的目的是爲了避免這個類被啓動類掃描到,如果被啓動類掃描到,會有父子上下文問題,如果父子上下文了,輕則配置不靈光了,重則服務啓動不來,對於RibbonConfiguration這個來說,如果父子上下文了,那麼它就自動變成全局的ribbon配置了,無法用於針對單個服務的特別配置
    在這裏插入圖片描述
package configuration;

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

@Configuration
public class RibbonConfiguration {

    @Bean
    public IRule ribbonRule(){
        return new RoundRobinRule();
    }
}

這裏配置的是輪詢規則

step2:新建個RibbonClientConfig類,類名不是固定的,大家隨意
在這裏插入圖片描述

package com.zengchen.content.config;

import configuration.RibbonConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Configuration;

@Configuration
@RibbonClients(defaultConfiguration = RibbonConfiguration.class)
public class RibbonClientConfig {
}

step3: 全局配置了輪詢規則,測試一下
http://localhost:8081/poem/testRibbon 接口測試,請求6次
user-center 兩個實例個被調用3次,基本證明全局配置生效。
在這裏插入圖片描述在這裏插入圖片描述
而此時,application.yml裏配置的是隨機規則 ,並沒有註釋,所以java代碼方式的配置優先級應該比屬性配置的方式高一些。但是這個意義不大,最好不要兩種方式同時配置!

user-center:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

3.5 自定義nacos權重負載均衡規則

  • content-center裏新寫個類NacosWeightedRule
@Slf4j
public class NacosWeightedRule extends AbstractLoadBalancerRule {

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    private Server choose(DynamicServerListLoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        String name = lb.getName();
        try {
            Instance instance = discoveryProperties.namingServiceInstance()
                    .selectOneHealthyInstance(name);
            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error("權重規則發生異常",e);
        }
        return null;
    }

    @Override
    public Server choose(Object key) {
        return choose((DynamicServerListLoadBalancer) getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }
}
  • 修改RibbonConfiguration返回新的NacosWeightedRule
@Configuration
public class RibbonConfiguration {

    @Bean
    public IRule ribbonRule(){
//        return new RoundRobinRule();
        return new NacosWeightedRule();
    }
}
  • 打開nacos控制檯修改兩個user-center服務實例的權重,8082端口的設置爲1,8083端口的設置爲4,這樣8083的服務實例會接收更多的請求訪問
    在這裏插入圖片描述
  • 重啓content-center服務,請求http://localhost:8081/poem/testRibbon ,請求20次
    結果:8082訪問了7次,8083訪問了13次
    第二次測試結果:8082被訪問了3次,8083被訪問了17次
    (如果把權重設置爲0,就不會有請求訪問了,可以做到優雅下線)
    在這裏插入圖片描述
    在這裏插入圖片描述

3.6 自定義同集羣優先訪問的負載均衡規則

  • 新建類NacosSameClusterWeightedRule
@Slf4j
public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule {

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    private Server choose(DynamicServerListLoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        try {
            // 本服務所屬集羣
            String clusterName = discoveryProperties.getClusterName();
            // 要請求的服務的名稱
            String targetInstanceName = lb.getName();
            // 服務相關Api
            NamingService namingService = discoveryProperties.namingServiceInstance();
            // 獲取目標服務所有實例(健康的實例)
            List<Instance> instances = namingService.selectInstances(targetInstanceName, true);
            // 獲得同集羣實例
            List<Instance> sameClusterInstances = instances.stream()
                    .filter(instance -> {
                        return Objects.equals(instance.getClusterName(), clusterName);
                    }).collect(Collectors.toList());
            // 等待被選擇的實例
            List<Instance> toBeChosenInstances = sameClusterInstances;
            // 如果沒有同集羣的實例,那就用所有實例
            if(CollectionUtils.isEmpty(sameClusterInstances)){
                toBeChosenInstances = instances;
                log.warn("發生跨集羣調用,集羣名稱={},目標服務名稱={}",clusterName,targetInstanceName);
            }
            // 根據權重從toBeChosenInstances選擇一個實例
            return new NacosServer(ExtendsBalancer.getInstanceByWeight(toBeChosenInstances));
        } catch (NacosException e) {
            log.error("同集羣負載規則發生異常", e);
        }
        return null;
    }

    @Override
    public Server choose(Object key) {
        return choose((DynamicServerListLoadBalancer) getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }
}

/**
 * <p>
 * 利用子類使用Balance的protected getHostByRandomWeight方法
 * </p>
 *
 * @author zengchen
 * @since 2019-08-11
 */
class ExtendsBalancer extends Balancer {
    public static Instance getInstanceByWeight(List<Instance> instances){
        return getHostByRandomWeight(instances);
    }

}
  • 修改RibbonConfiguration返回新的NacosSameClusterWeightedRule
  • 測試前置準備
    啓動三個user-center實例,兩個SH集羣的,一個BJ集羣的
    8082:SH,權重 1
    8083:SH,權重 4
    8084:BJ, 權重 1
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 118.31.11.178:8848
          # 所屬集羣 上海
          cluster-name: SH

在這裏插入圖片描述
content-center 啓動一個屬於SH
在這裏插入圖片描述

  • 測試
    請求http://localhost:8081/poem/testRibbon ,請求20
    8082:SH,權重 1,被訪問2次
    8083:SH,權重 4,被訪問18次
    8084:BJ, 權重 1,被訪問0次
    符合預期!
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述

3.7 自定義根據服務實例元數據的負載均衡規則

可以實現v1版本的content-center只調用v1版本user-center
可以實現v2版本的content-center只調用v2版本user-center

  • 給content-center配置元數據 self-version: v1target-version: v1
    application.yml
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 118.31.11.178:8848
        # 集羣名稱
        cluster-name: SH
        # 元數據
        metadata:
          self-version: v1
          target-version: v1
  • 新建類 NacosClusterMetadataWeightedRule
@Slf4j
public class NacosClusterMetadataWeightedRule extends AbstractLoadBalancerRule {

    @Autowired
    private NacosDiscoveryProperties discoveryProperties;

    // 自己的版本號
    private final static String SELF_VERSION = "self-version";
    // 需要調用的版本號
    private final static String TARGET_VERSION = "target-version";

    private Server choose(DynamicServerListLoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        }
        try {
            // 本服務目標版本號
            String targetVersion = discoveryProperties.getMetadata().get(SELF_VERSION);
            // 本服務所屬集羣
            String clusterName = discoveryProperties.getClusterName();
            // 要請求的服務的名稱
            String targetInstanceName = lb.getName();
            // 服務相關Api
            NamingService namingService = discoveryProperties.namingServiceInstance();
            // 獲取目標服務所有實例(健康的實例)
            List<Instance> instances = namingService.selectInstances(targetInstanceName, true);
            List<Instance> versionMatchInstances = instances;
            // 獲得targetVersion的實例
            if(StringUtils.isNotBlank(targetVersion)){
                versionMatchInstances = instances.stream()
                        .filter(instance -> {
                            return Objects.equals(instance.getMetadata().get(SELF_VERSION),TARGET_VERSION);
                        }).collect(Collectors.toList());
                if(CollectionUtils.isEmpty(versionMatchInstances)){
                    log.warn("未找到元數據匹配的目標實例!請檢查配置。targetVersion = {}, instance = {}", targetVersion, instances);
                    return null;
                }
            }

            // 從元數據匹配的實例中獲得同集羣實例
            List<Instance> clusterMetadataMatchInstances = versionMatchInstances;
            if(StringUtils.isNotBlank(clusterName)){
                clusterMetadataMatchInstances =  versionMatchInstances.stream()
                        .filter(instance -> {
                            return Objects.equals(instance.getClusterName(), clusterName);
                        }).collect(Collectors.toList());
                if(CollectionUtils.isEmpty(clusterMetadataMatchInstances)){
                    log.warn("發生跨集羣調用。clusterName = {}, targetVersion = {}, clusterMetadataMatchInstances = {}", clusterName, targetVersion, clusterMetadataMatchInstances);
                    clusterMetadataMatchInstances = versionMatchInstances;
                }
            }
            // 根據權重從toBeChosenInstances選擇一個實例
            return new NacosServer(ExtendsBalancer.getInstanceByWeight(clusterMetadataMatchInstances));
        } catch (NacosException e) {
            log.error("元數據同集羣負載規則發生異常", e);
        }
        return null;
    }

    @Override
    public Server choose(Object key) {
        return choose((DynamicServerListLoadBalancer) getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }
}
  • 修改RibbonConfiguration返回新的NacosClusterMetadataWeightedRule
@Configuration
public class RibbonConfiguration {

    @Bean
    public IRule ribbonRule(){
//        return new RoundRobinRule();
//        return new NacosWeightedRule();
//        return new NacosSameClusterWeightedRule();
        return new NacosClusterMetadataWeightedRule();
    }
}
  • 測試前置準備
    啓動四個user-center實例,三個SH集羣的,一個BJ集羣的
    8082:SH,權重 1,self-version v1
    8083:SH,權重 4,self-version v1
    8084:SH,權重 1,self-version v2
    8085:BJ, 權重 1,self-version v1
    在這裏插入圖片描述
  • 測試
    請求http://localhost:8081/poem/testRibbon ,請求20
    8082:SH,權重 1,self-version v1,被訪問2
    8083:SH,權重 4,self-version v1,被訪問18
    8084:SH,權重 1,self-version v2,被訪問0
    8085:BJ, 權重 1,self-version v1,被訪問0
    符合預期!
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述

4.飢餓加載

第一次調用user-center服務,速度都很慢。因爲ribbon是懶加載模式,可以通過屬性配置方式改成飢餓模式,這樣第一次和後面再次請求的速度就沒有區別了
application.yml

ribbon:
  # 飢餓加載
  eager-load:
    # 開啓
    enabled: true
    # 哪些服務,英文逗號隔開
    clients: user-center
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章