天天看點

spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題

Ribbon實作負載均衡

  • 一、實作方式
  • 二、手寫用戶端側負載均衡器
  • 三、使用Ribbon實作負載均衡
  • 四、Ribbon 組成
  • 五、Ribbon 配置
  • 六、Ribbon 饑餓加載
  • 七、Ribbon擴充
  • 八、現有架構存在問題

一、實作方式

  • 伺服器端負載均衡(單體架構)
    spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題
  • 用戶端側負載均衡(内容中心相對使用者中心,屬于用戶端,可在内容中心叢集中配置負載均衡規則)
    spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題

二、手寫用戶端側負載均衡器

  • 随機負載均衡算法
    spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題

三、使用Ribbon實作負載均衡

1、定義:Netfix開源的用戶端側負載均衡器,即提供了負載均衡算法

2、架構演進:

spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題

3、整合Ribbon

  • 加依賴:不需要,spring-cloud-starter-alibaba-nacos-discovery已預設提供
    spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題
  • 加注解
spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題
  • 寫配置:無配置

PS:代碼改寫,使用http://user-center/users/{userId},Ribbon會自動讀取名稱,進行位址查找并轉換

spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題

四、Ribbon 組成

spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題

1、内置負載均衡規則

spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題

PS:預設為ZoneAvoidanceRule,無Zone環境,等價于RoundRobinRule

五、Ribbon 配置

1、java代碼配置,采用随機算法

  • 建立RibbonConfiguration 配置類
package com.hzb2i.contentcenter.configuration.Ribbon;

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

@Configuration
public class RibbonConfiguration {
    @Bean
    public IRule ribbonRule(){
        return new RandomRule();
    }
}
           
  • 建立 UserCenterRibbonConfiguration 類
package com.hzb2i.contentcenter.configuration;

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

@Configuration
@RibbonClient(name = "user-center",configuration = RibbonConfiguration.class)
public class UserCenterRibbonConfiguration {
}

           

PS:需要注意UserCenterRibbonConfiguration 需要放到@SpringBootApplication啟動類掃描不到的包下

spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題
  • @SpringBootApplication注解預設掃描目前的包和子包
  • 若将UserCenterRibbonConfiguration 放到啟動類相同路徑下,将會出現@SpringBootApplication 主上下文和UserCenterRibbonConfiguration 類的@Configuration 子上下文出現重複掃描,UserCenterRibbonConfiguration 的配置為全局配置,将會被所有RibbonClient共享,即不管内容中心通路使用者中心還是其他的微服務,都會采用UserCenterRibbonConfiguration 配置(重複掃描帶來的問題,感興趣可看此分享:spring重複掃描事務失敗)

2、用配置屬性配置:application.yml加入以下配置

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

PS:兩種配置方式對比

spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題

PS:寫配置方式,線上無需重新打包,采用的是讀取外部配置檔案方式

3、最佳實踐:

  • 盡量使用屬性配置,屬性配置實作不了情況再考慮用代碼配置
  • 在同一個微服務内盡量保持單一性,比如統一使用屬性配置,不要用兩種方式混用,增加定位代碼的複雜性

4、全局配置:

  • 方式一:讓ComponentScan重疊,即将RibbonConfiguration放到啟動類相同路徑(不建議使用,會導緻項目無法啟動)
  • 正确途徑
    spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題
    5、Ribbon其他配置項
  • 代碼配置:
    spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題
  • 寫配置:
    spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題

六、Ribbon 饑餓加載

  • 問題:Ribbon預設采用懶加載,隻有http://user-center/users/{userId}執行,才會使用Ribbon自動擷取使用者中心位址,導緻第一次通路加載速度慢
  • 措施:寫配置,開啟饑餓加載
    spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題

七、Ribbon擴充

1、支援Nacos權重(根據權重判斷的負載均衡規則):

  • 場景:若微服務有衆多執行個體,分别部署到不同伺服器上,可以把在性能好的機器上的執行個體權重設高點,性能差的機器的執行個體權重設低點,這樣權重高的執行個體被調用的幾率就高一些
  • ribbon預設不支援nacos權重,需通過以下方式開發

    ① 可實作IRule接口

    ② 繼承AbstractLoadBalancerRule抽象類,并将此負載均衡算法設為全局配置

package com.hzb2i.contentcenter.configuration;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

/*
 * ribbon 擴充:支撐nacos權重
 */
@Slf4j
public class NacosWeightRule extends AbstractLoadBalancerRule {
    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {
        //讀取配置,并初始化NacosWeightRule
    }

    @Override
    public Server choose(Object o) {
        try {
            //入口
            BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
            //想要請求的微服務名稱
            String name = loadBalancer.getName();
            //實作負載均衡算法
            //拿到服務發現的相關API
            NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
            // nacos client 自動通過基于權重的負責均衡算法,給我們選擇一個執行個體
            Instance instance =  namingService.selectOneHealthyInstance(name);
            log.info("選擇的執行個體:port={},instance={}",instance.getPort(),instance);
            return new NacosServer(instance);
        } catch (NacosException e) {
            e.printStackTrace();
        }
        return null;
    }
}
           

在nacos控制台修改權重:

spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題

啟動使用者中心、内容中心測試:請求基本調用權重高的執行個體

spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題

PS:nacos client内置了基于權重的負載均衡算法,spring cloud Alibaba為何還要整合ribbon,直接用nacos client不就行了嗎?主要是為符合spring cloud标準,spring cloud有個子項目spring cloud commons(定義了标準),其下邊的子項目spring cloud loadbalancer(無權重概念),spring cloud Alibaba符合此标準,故未整合權重的算法

PS:其他實作權重負載均衡算法方式請移步 擴充Ribbon支援Nacos權重的三種方式

2、同叢集優先調用

  • 場景:如存在北京機房和南京機房,想讓北京機房的内容中心有限調用同機房的使用者中心,在同機房使用者中心不存在的情況下再調用南京機房的使用者中心,可以采用此方式
  • 代碼開發,并将此負載均衡算法設為全局配置:
package com.hzb2i.contentcenter.configuration;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.client.naming.core.Balancer;
import com.alibaba.nacos.client.naming.utils.Chooser;
import com.alibaba.nacos.client.naming.utils.Pair;
import com.alibaba.nacos.client.utils.LogUtils;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class NacosSameClusterWeightRule extends AbstractLoadBalancerRule {

    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    @Override
    public Server choose(Object o) {
        try {
            String clusterName = nacosDiscoveryProperties.getClusterName();
            //入口
            BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
            //想要請求的微服務名稱
            String name = loadBalancer.getName();
            //實作負載均衡算法
            //拿到服務發現的相關API
            NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
            //找到指定服務的所有API
            List<Instance> instances = namingService.selectInstances(name,true);
            //過濾相同叢集的所有執行個體
            List<Instance> sameClusterInstances = instances.stream().filter(instance -> {
                return Objects.equals(instance.getClusterName(),clusterName);
            }).collect(Collectors.toList());
            //如果B(sameClusterInstances)是空,否則用A(instances)
            List<Instance> instancesToBeChosen = new ArrayList<>();
            if(CollectionUtils.isEmpty(sameClusterInstances)){
                instancesToBeChosen = instances;
                log.info("跨叢集調用:name={},clusterName={},instances={}",name,clusterName,instances);
            }else {
                instancesToBeChosen = sameClusterInstances;
                log.info("同叢集調用:name={},clusterName={},instances={}",name,clusterName,sameClusterInstances);
            }
            //基于權重的負載均衡算法,傳回一個執行個體(namingService無根據list直接傳回執行個體)
            Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToBeChosen);
            return new NacosServer(instance);
        } catch (NacosException e) {
            e.printStackTrace();
        }
        return null;
    }
}
class ExtendBalancer extends Balancer {
    public static Instance getHostByRandomWeight2(List<Instance> hosts) {
        LogUtils.NAMING_LOGGER.debug("entry randomWithWeight");
        if (hosts != null && hosts.size() != 0) {
            LogUtils.NAMING_LOGGER.debug("new Chooser");
            List<Pair<Instance>> hostsWithWeight = new ArrayList();
            Iterator var2 = hosts.iterator();

            while(var2.hasNext()) {
                Instance host = (Instance)var2.next();
                if (host.isHealthy()) {
                    hostsWithWeight.add(new Pair(host, host.getWeight()));
                }
            }

            LogUtils.NAMING_LOGGER.debug("for (Host host : hosts)");
            Chooser<String, Instance> vipChooser = new Chooser("www.taobao.com");
            vipChooser.refresh(hostsWithWeight);
            LogUtils.NAMING_LOGGER.debug("vipChooser.refresh");
            return (Instance)vipChooser.randomWithWeight();
        } else {
            LogUtils.NAMING_LOGGER.debug("hosts == null || hosts.size() == 0");
            return null;
        }
    }
}
           

啟動測試:優先通路同叢集的端口,同叢集微服務挂了,才進行跨叢集通路

spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題
spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題

3、基于中繼資料的版本控制:擴充Ribbon支援基于中繼資料的版本管理

PS:深入了解Nacos NameSpace:微服務隻能調用同一空間的執行個體

八、現有架構存在問題

spring cloud Alibaba 實作負載均衡 Ribbon學習筆記五一、實作方式二、手寫用戶端側負載均衡器三、使用Ribbon實作負載均衡四、Ribbon 組成五、Ribbon 配置六、Ribbon 饑餓加載七、Ribbon擴充八、現有架構存在問題

繼續閱讀