Ribbon的負(fù)載均衡策略、原理和擴(kuò)展

ribbon 是一個客戶端負(fù)載均衡器,可以簡單的理解成類似于 nginx的負(fù)載均衡模塊的功能。

Load Balance負(fù)載均衡是用于解決一臺機(jī)器(一個進(jìn)程)無法解決所有請求而產(chǎn)生的一種算法。像nginx可以使用負(fù)載均衡分配流量,ribbon為客戶端提供負(fù)載均衡,dubbo服務(wù)調(diào)用里的負(fù)載均衡等等,很多地方都使用到了負(fù)載均衡。

主流的Load Balance方案可分成兩類:

一種是集中式Load Balance, 即在服務(wù)的消費(fèi)方和提供方之間使用獨(dú)立的LB設(shè)施(可以是硬件,如F5, 也可以是軟件,如nginx), 由該設(shè)施負(fù)責(zé)把訪問請求通過某種策略轉(zhuǎn)發(fā)至服務(wù)的提供方;

另一種是進(jìn)程內(nèi)Load Balance,將LB邏輯集成到消費(fèi)方,消費(fèi)方從服務(wù)注冊中心獲知有哪些地址可用,然后自己再從這些地址中選擇出一個合適的服務(wù)器。Ribbon就屬于后者,它只是一個類庫,集成于消費(fèi)方進(jìn)程,消費(fèi)方通過它來獲取到服務(wù)提供方的地址。

使用負(fù)載均衡帶來的好處很明顯:

當(dāng)集群里的1臺或者多臺服務(wù)器down的時候,剩余的沒有down的服務(wù)器可以保證服務(wù)的繼續(xù)使用
使用了更多的機(jī)器保證了機(jī)器的良性使用,不會由于某一高峰時刻導(dǎo)致系統(tǒng)cpu急劇上升
負(fù)載均衡有好幾種實(shí)現(xiàn)策略,常見的有:

  • 隨機(jī) (Random)
  • 輪詢 (RoundRobin)
  • 一致性哈希 (ConsistentHash)
  • 哈希 (Hash)
  • 加權(quán)(Weighted)

Ribbon的組成:

接口 作用 默認(rèn)值
IClientConfig 讀取配置 DefaultClientConfigImpl
IRule 負(fù)載均衡規(guī)則,選擇實(shí)例 ZoneAvoidanceRule
IPing 篩選掉ping不通的實(shí)例 DummyPing
ServerList<Server> 交給Ribbon的實(shí)例列表 Ribbon:ConfigurationBasedServerList Spring Cloud Alibaba:NacosServerList
ServerListFilter<Server> 過濾掉不符合條件的實(shí)例 ZonePreferenceServerListFilter
ILoadBalance Ribbon的入口 ZoneAwareLoadBalance
ServerListUpdater 更新交給Ribbon的List的策略 PollingServerListUpdater

Ribbon是比較靈活的,它對所有的組件都定義成了接口,如果對默認(rèn)值不滿意,可以實(shí)現(xiàn)這些接口配置一下,就可以將默認(rèn)實(shí)現(xiàn)替換掉。

ILoadBalance 負(fù)載均衡器

ribbon是一個為客戶端提供負(fù)載均衡功能的服務(wù),它內(nèi)部提供了一個叫做ILoadBalance的接口代表負(fù)載均衡器的操作,比如有添加服務(wù)器操作、選擇服務(wù)器操作、獲取所有的服務(wù)器列表、獲取可用的服務(wù)器列表等等。

ILoadBalance的實(shí)現(xiàn)類如下:

image.png

負(fù)載均衡器是從服務(wù)發(fā)現(xiàn)組件(NacosDiscoveryClient或EurekaClient)(DiscoveryClient的實(shí)現(xiàn)類為NacosDiscoveryClient)獲取服務(wù)信息,根據(jù)IRule去路由,并且根據(jù)IPing判斷服務(wù)的可用性。

負(fù)載均衡器多久一次去獲取一次從DiscoveryClient獲取注冊信息呢?在BaseLoadBalancer類下,BaseLoadBalancer的構(gòu)造函數(shù),該構(gòu)造函數(shù)開啟了一個PingTask任務(wù)setupPingTask();,代碼如下:

public BaseLoadBalancer(String name, IRule rule, LoadBalancerStats stats, IPing ping, IPingStrategy pingStrategy) {
        this.rule = DEFAULT_RULE;
        this.pingStrategy = DEFAULT_PING_STRATEGY;
        this.ping = null;
        this.allServerList = Collections.synchronizedList(new ArrayList());
        this.upServerList = Collections.synchronizedList(new ArrayList());
        this.allServerLock = new ReentrantReadWriteLock();
        this.upServerLock = new ReentrantReadWriteLock();
        this.name = "default";
        this.lbTimer = null;
        this.pingIntervalSeconds = 10;
        this.maxTotalPingTimeSeconds = 5;
        this.serverComparator = new ServerComparator();
        this.pingInProgress = new AtomicBoolean(false);
        this.counter = Monitors.newCounter("LoadBalancer_ChooseServer");
        this.enablePrimingConnections = false;
        this.changeListeners = new CopyOnWriteArrayList();
        this.serverStatusListeners = new CopyOnWriteArrayList();
        logger.debug("LoadBalancer [{}]:  initialized", name);
        this.name = name;
        this.ping = ping;
        this.pingStrategy = pingStrategy;
        this.setRule(rule);
        this.setupPingTask();
        this.lbStats = stats;
        this.init();
    }

setupPingTask()的具體代碼邏輯,它開啟了ShutdownEnabledTimer執(zhí)行PingTask任務(wù),在默認(rèn)情況下pingIntervalSeconds為10,即每10秒鐘,向EurekaClient發(fā)送一次”ping”。

void setupPingTask() {
        if (!this.canSkipPing()) {
            if (this.lbTimer != null) {
                this.lbTimer.cancel();
            }

            this.lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + this.name, true);
            this.lbTimer.schedule(new BaseLoadBalancer.PingTask(), 0L, (long)(this.pingIntervalSeconds * 1000));
            this.forceQuickPing();
        }
    }

PingTask源碼,即new一個Pinger對象,并執(zhí)行runPinger()方法。

查看Pinger的runPinger()方法,最終根據(jù) pingerStrategy.pingServers(ping, allServers)來獲取服務(wù)的可用性,如果該返回結(jié)果,如之前相同,則不去向EurekaClient獲取注冊列表,如果不同則通知ServerStatusChangeListener或者changeListeners發(fā)生了改變,進(jìn)行更新或者重新拉取。

完整過程是:
LoadBalancerClient(RibbonLoadBalancerClient是實(shí)現(xiàn)類)在初始化的時候(execute方法),會通過ILoadBalance(BaseLoadBalancer是實(shí)現(xiàn)類)向Eureka注冊中心獲取服務(wù)注冊列表,并且每10s一次向EurekaClient或NacosClient發(fā)送“ping”,來判斷服務(wù)的可用性,如果服務(wù)的可用性發(fā)生了改變或者服務(wù)數(shù)量和之前的不一致,則從注冊中心更新或者重新拉取。LoadBalancerClient有了這些服務(wù)注冊列表,就可以根據(jù)具體的IRule來進(jìn)行負(fù)載均衡。

IRule 路由

IRule接口代表負(fù)載均衡策略:

public interface IRule {
    Server choose(Object var1);
    void setLoadBalancer(ILoadBalancer var1);
    ILoadBalancer getLoadBalancer();
}

IRule接口的實(shí)現(xiàn)類有以下幾種:

image.png

Ribbon內(nèi)置的負(fù)載均衡規(guī)則:

規(guī)則名稱 特點(diǎn)
AvailabilityFilteringRule 過濾掉一直連接失敗的被標(biāo)記為circuit tripped的后端Server,并
過濾掉那些高并發(fā)的后端Server或者使用一個AvailabilityPredicate
來包含過濾server的邏輯,其實(shí)就是檢查status里記錄的各個server
的運(yùn)行狀態(tài)
BestAvailableRule 選擇一個最小的并發(fā)請求的server,逐個考察server,
如果Server被tripped了,則跳過
RandomRule 隨機(jī)選擇一個Server
ResponseTimeWeightedRule 已廢棄,作用同WeightedResponseTimeRule
WeightedResponseTimeRule 根據(jù)響應(yīng)時間加權(quán),響應(yīng)時間越長,權(quán)重越小,被選中的可能性越低
RetryRule 對選定的負(fù)載均衡策略加上重試機(jī)制,在一個配置時間段內(nèi)當(dāng)
選擇Server不成功,則一直嘗試使用subRule的方式選擇一個
可用的Server
RoundRobinRule 輪詢選擇,輪詢index,選擇index對應(yīng)位置的Server
ZoneAvoidanceRule 默認(rèn)的負(fù)載均衡策略,即復(fù)合判斷Server所在區(qū)域的性能和Server的可用性
選擇Server,在沒有區(qū)域的環(huán)境下,類似于輪詢(RandomRule)

其中RandomRule表示隨機(jī)策略、RoundRobinRule表示輪詢策略、WeightedResponseTimeRule表示加權(quán)策略、BestAvailableRule表示請求數(shù)最少策略等等。

隨機(jī)策略很簡單,就是從服務(wù)器中隨機(jī)選擇一個服務(wù)器,RandomRule的實(shí)現(xiàn)代碼如下:

public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            return null;
        } else {
            Server server = null;

            while(server == null) {
                if (Thread.interrupted()) {
                    return null;
                }

                List<Server> upList = lb.getReachableServers();
                List<Server> allList = lb.getAllServers();
                int serverCount = allList.size();
                if (serverCount == 0) {
                    return null;
                }

                int index = this.chooseRandomInt(serverCount) ;//隨機(jī)獲取索引值index
                server = (Server)upList.get(index); // 得到服務(wù)器實(shí)例
                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }

                    server = null;
                    Thread.yield();
                }
            }

            return server;
        }
    }
protected int chooseRandomInt(int serverCount) {
    return ThreadLocalRandom.current().nextInt(serverCount);
}

public Server choose(Object key) {
    return this.choose(this.getLoadBalancer(), key);
}

RoundRobinRule輪詢策略表示每次都取下一個服務(wù)器,比如一共有5臺服務(wù)器,第1次取第1臺,第2次取第2臺,第3次取第3臺,以此類推:

WeightedResponseTimeRule繼承了RoundRobinRule,開始的時候還沒有權(quán)重列表,采用父類的輪詢方式,有一個默認(rèn)每30秒更新一次權(quán)重列表的定時任務(wù),該定時任務(wù)會根據(jù)實(shí)例的響應(yīng)時間來更新權(quán)重列表,choose方法做的事情就是,用一個(0,1)的隨機(jī)double數(shù)乘以最大的權(quán)重得到randomWeight,然后遍歷權(quán)重列表,找出第一個比randomWeight大的實(shí)例下標(biāo),然后返回該實(shí)例,代碼略。

BestAvailableRule策略用來選取最少并發(fā)量請求的服務(wù)器:

public Server choose(Object key) {
        if (loadBalancerStats == null) {
            return super.choose(key);
        }
        List<Server> serverList = getLoadBalancer().getAllServers();
        int minimalConcurrentConnections = Integer.MAX_VALUE;
        long currentTime = System.currentTimeMillis();
        Server chosen = null;
        for (Server server: serverList) {
            ServerStats serverStats = loadBalancerStats.getSingleServerStat(server);
            if (!serverStats.isCircuitBreakerTripped(currentTime)) {
                int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);
                if (concurrentConnections < minimalConcurrentConnections) {
                    minimalConcurrentConnections = concurrentConnections;
                    chosen = server;
                }
            }
        }
        if (chosen == null) {
            return super.choose(key);
        } else {
            return chosen;
        }
    }

使用Ribbon提供的負(fù)載均衡策略很簡單,只需以下幾部:

1、創(chuàng)建具有負(fù)載均衡功能的RestTemplate實(shí)例

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

使用RestTemplate進(jìn)行rest操作的時候,會自動使用負(fù)載均衡策略,它內(nèi)部會在RestTemplate中加入LoadBalancerInterceptor這個攔截器,這個攔截器的作用就是使用負(fù)載均衡。
默認(rèn)情況下會采用輪詢策略,如果希望采用其它策略,則指定IRule實(shí)現(xiàn),如:

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

這種方式對Feign也有效。

2、實(shí)現(xiàn)Ribbon細(xì)粒度的配置,即如果微服務(wù)order-center調(diào)用微服務(wù)user-center和微服務(wù)goods-center,order-center調(diào)用user-center使用隨機(jī),order-center調(diào)用goods-center使用默認(rèn)ZoneAvoidanceRule

  • Java代碼配置
@Configuration
@RibbonClient(name="user-center",configuration = RibbonConfiguration.class)
public class UserCenterRibbonConfiguration {
}

在SpringBoot啟動類以外新建ribbonconfiguration包,并新建RibbonConfiguration類

/**
 * @author: huangyibo
 * @Date: 2019/11/2 18:08
 * @Description: 如果將此類放進(jìn)啟動類的包下,那么此工程的所有ribbon都會使用這種負(fù)載均衡規(guī)則
 */

@Configuration
public class RibbonConfiguration {

    //Ribbon提供的負(fù)載均衡策略
    @Bean
    public IRule ribbonRule(){
        return new RandomRule();
    }
}
  • 用配置屬性配置
# 通過配置文件指定user-center實(shí)例的ribbon負(fù)載均衡策略為RandomRule,和java代碼方式指定效果一樣
user-center:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
  • Ribbon細(xì)粒度配置最佳實(shí)踐總結(jié)
    代碼配置方式 VS 屬性配置方式


    image.png

1)、盡量使用屬性配置,屬性方式實(shí)現(xiàn)不了的情況下再考慮使用代碼配置
2)、在同一個微服務(wù)內(nèi)盡量保持單一性,比如統(tǒng)一使用屬性配置,不要兩種方式混用,增加代碼定位的復(fù)雜性

3、實(shí)現(xiàn)Ribbon的全局配置

@Configuration
@RibbonClients(defaultConfiguration = RibbonConfiguration.class)//Ribbon負(fù)載均衡的全局配置
public class UserCenterRibbonConfiguration {
}

4、前面表格中的Ribbon的組成每一項(xiàng)都可以自定義,例如:

  • Java代碼配置
@Configuration
public class RibbonConfiguration {

    //Ribbon提供的負(fù)載均衡策略
    @Bean
    public IRule ribbonRule(){
        return new RandomRule();
    }

    @Bean
    public IPing ping(){
        return new PingUrl();
    }
}
  • 用配置屬性配置
    clientName.ribbon.如下屬性:
  • NFLoadBalancerClassName:ILoadBalancer實(shí)現(xiàn)類
  • NFLoadBalancerRuleClassName:IRule實(shí)現(xiàn)類
  • NFLoadBalancerPingClassName:IPing實(shí)現(xiàn)類
  • NIWSServerListClassName:ServerList實(shí)現(xiàn)類
  • NIWSServerListFilterClassName:ServerListFilter實(shí)現(xiàn)類

5、Ribbon的饑餓加載,Ribbon默認(rèn)是懶加載的

ribbon:
  eager-load:
    # 開啟ribbon饑餓加載
    enabled: true
    # 配置user-center使用ribbon饑餓加載,多個使用逗號分隔
    clients: user-center

6、擴(kuò)展Ribbon,支持Nocas權(quán)重

  • 編輯Nacos權(quán)重


    image.png

新建NacosWeightedRule

/**
 * @author: huangyibo
 * @Date: 2019/11/2 18:44
 * @Description: 繼承AbstractLoadBalancerRule編寫負(fù)載均衡算法,支持Nacos的權(quán)重
 */

@Slf4j
public class NacosWeightedRule extends AbstractLoadBalancerRule {

    /**
     * NacosDiscoveryProperties內(nèi)置了基于權(quán)重的負(fù)載均衡算法
     */
    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;

    /**
     * 讀取配置文件并初始化NacosWeightedRule
     * @param iClientConfig
     */
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }
    
    /**
     * 實(shí)現(xiàn)基于權(quán)重的負(fù)載均衡算法
     * @param o
     */
    @Override
    public Server choose(Object o) {
        try {
            BaseLoadBalancer loadBalancer = (BaseLoadBalancer)this.getLoadBalancer();
            log.info("loadBalancer={}",loadBalancer);
            //想要請求的微服務(wù)名稱
            String name = loadBalancer.getName();
            //拿到服務(wù)發(fā)現(xiàn)新的相關(guān)的api
            NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
            //Nacos client自動通過基于權(quán)重的負(fù)載均衡算法,給我們選擇一個實(shí)例
            Instance instance = namingService.selectOneHealthyInstance(name);
            log.info("Nacos client選擇的實(shí)例:port={} , instance={}",instance.getPort(),instance);
            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error("Nacos client自動通過基于權(quán)重的負(fù)載均衡算法,選擇微服務(wù)實(shí)例異常,e={}",e);
            return null;
        }
    }
}

//spring cloud commons ---> 定義了標(biāo)準(zhǔn)
//spring cloud loadbalancer --->定義了各種負(fù)載均衡器的標(biāo)準(zhǔn) 沒有權(quán)重

配置NacosWeightedRule

@Configuration
public class RibbonConfiguration {

    //自定義負(fù)載均衡配置,通過Nacos client自動通過基于權(quán)重的負(fù)載均衡算法,給我們選擇一個實(shí)例
    @Bean
    public IRule ribbonRule(){
        return new NacosWeightedRule();
    }
}

@Configuration
@RibbonClients(defaultConfiguration = RibbonConfiguration.class)//Ribbon負(fù)載均衡的全局配置
public class UserCenterRibbonConfiguration {
}

7、擴(kuò)展Ribbon,實(shí)現(xiàn)Nacos注冊中心同一集群優(yōu)先調(diào)用

新建NacosSameClusterWeightedRule

/**
 * @author: huangyibo
 * @Date: 2019/11/2 19:03
 * @Description: 繼承AbstractLoadBalancerRule拓展Ribbon,進(jìn)行同一集群下服務(wù)優(yōu)先調(diào)用,這個是針對的異地災(zāi)備的
 */
@Slf4j
public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule {

    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    @Override
    public Server choose(Object o) {
        try {
            //拿到配置文件中的集群名稱 shenzhen
            String clusterName = nacosDiscoveryProperties.getClusterName();

            BaseLoadBalancer loadBalancer = (BaseLoadBalancer)this.getLoadBalancer();
            log.info("loadBalancer={}",loadBalancer);
            //想要請求的微服務(wù)名稱
            String name = loadBalancer.getName();
            //拿到服務(wù)發(fā)現(xiàn)新的相關(guān)的api
            NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();

            //1、找到指定服務(wù)的所有實(shí)例 A
            List<Instance> instances = namingService.selectInstances(name, true);
            //instances.get(0).getMetadata();//獲取實(shí)例的元數(shù)據(jù)

            //2、過濾出相同集群下的所有實(shí)例 B
            List<Instance> sameClustInstances = instances.stream()
                    .filter(instance -> Objects.equals(instance.getClusterName(), clusterName))
                    .collect(Collectors.toList());

            //3、如果B是空,就用A
            List<Instance> instancesToBeChosen = new ArrayList<>();
            if(CollectionUtils.isEmpty(sameClustInstances)){
                instancesToBeChosen = instances;
                log.warn("發(fā)生跨集群的調(diào)用,name={},clusterName={},instances={}",name,clusterName,instances);
            }else {
                instancesToBeChosen = sameClustInstances;
            }

            //4、基于權(quán)重的負(fù)載均衡算法,返回1個實(shí)例
            Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToBeChosen);
            log.info("選擇的實(shí)例是:port={},instance={}",instance.getPort(),instance);
            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error("發(fā)生異常了,e={}",e);
            return null;
        }
    }
}

//當(dāng)Balancer類下面的getHostByRandomWeight方法不能直接調(diào)用的時候,繼承它然后去調(diào)用
class ExtendBalancer extends Balancer{
    public static Instance getHostByRandomWeight2(List<Instance> hosts) {
        return getHostByRandomWeight(hosts);
    }
}

配置application.yml

spring:
    cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
            cluster-name: shanghai

配置NacosSameClusterWeightedRule

@Configuration
public class RibbonConfiguration {

    //自定義負(fù)載均衡配置,通過Nacos client拓展Ribbon,進(jìn)行同一集群下服務(wù)優(yōu)先調(diào)用,這個是針對的異地災(zāi)備的
    @Bean
    public IRule ribbonRule(){
        return new NacosSameClusterWeightedRule();
    }
}

8、擴(kuò)展Ribbon,實(shí)現(xiàn)Nacos注冊中心基于元數(shù)據(jù)的版本控制

Nacos元數(shù)據(jù)的配置:

spring:
    cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
            cluster-name: shanghai
            metadata:
                # 自己這個實(shí)例的版本
                version: V1
                # 允許調(diào)用的提供者版本
                target-version: V1

新建NacosRule實(shí)現(xiàn)AbstractLoadBalancerRule

@Slf4j
public class NacosRule extends AbstractLoadBalancerRule {

    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    @Override
    public Server choose(Object o) {
        try {
            //負(fù)載均衡規(guī)則:優(yōu)先調(diào)用同一集群下,符合metadata元數(shù)據(jù)的實(shí)例
            //如果沒有,就選擇所有集群下,符合metadata的實(shí)例

            //1、查詢所有實(shí)例 A
            //2、篩選元數(shù)據(jù)匹配的實(shí)例 B
            //3、篩選出同cluster下元數(shù)據(jù)匹配的實(shí)例 C
            //4、如果C為空,就用B
            //5、隨機(jī)選擇實(shí)例

            String clusterName = nacosDiscoveryProperties.getClusterName();
            String targerVersion = nacosDiscoveryProperties.getMetadata().get("target-version");

            DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer)this.getLoadBalancer();
            //想要請求的微服務(wù)名稱
            String name = loadBalancer.getName();
            //拿到服務(wù)發(fā)現(xiàn)新的相關(guān)的api
            NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();

            //所有實(shí)例
            List<Instance> instances = namingService.selectInstances(name, true);

            List<Instance> metadataMatchInstances = new ArrayList<>();
            //如果配置了版本映射,那么只調(diào)用元數(shù)據(jù)匹配的實(shí)例
            if(!StringUtils.isEmpty(targerVersion)){
                metadataMatchInstances = instances.stream()
                        .filter(instance -> Objects.equals(targerVersion,instance.getMetadata().get("target-version")))
                        .collect(Collectors.toList());
                if(CollectionUtils.isEmpty(metadataMatchInstances)){
                    log.warn("未找到元數(shù)據(jù)匹配的目標(biāo)實(shí)例!請檢查配置,目標(biāo)元數(shù)據(jù)配置為:targerVersion={}",targerVersion);
                    return null;
                }
            }

            List<Instance> clusterMetadataMatchInstances = new ArrayList<>();
            //如果配置了集群名稱,需篩選同集群下元數(shù)據(jù)匹配的實(shí)例
            if(!StringUtils.isEmpty(clusterName)){
                clusterMetadataMatchInstances = metadataMatchInstances.stream()
                        .filter(instance -> Objects.equals(clusterName,instance.getClusterName()))
                        .collect(Collectors.toList());
                if(CollectionUtils.isEmpty(clusterMetadataMatchInstances)){
                    clusterMetadataMatchInstances = metadataMatchInstances;
                    log.warn("發(fā)生跨集群的調(diào)用,name={},clusterName={},targerVersion={},instances={}",name,clusterName,targerVersion,instances);
                }
            }

            Instance instance = ExtendBalancer.getHostByRandomWeight2(clusterMetadataMatchInstances);
            log.info("選擇的實(shí)例是:port={},instance={}",instance.getPort(),instance);
            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error("發(fā)生異常了,e={}",e);
            return null;
        }
    }
}

負(fù)載均衡算法:

public class ExtendBalancer extends Balancer {
    /**
     * 根據(jù)權(quán)重,隨機(jī)選擇實(shí)例
     *
     * @param instances 實(shí)例列表
     * @return 選擇的實(shí)例
     */
    public static Instance getHostByRandomWeight2(List<Instance> instances) {
        return getHostByRandomWeight(instances);
    }
}

參考:

https://blog.csdn.net/wudiyong22/article/details/80829808

http://www.imooc.com/article/288674

https://blog.csdn.net/rickiyeat/article/details/64918756

https://www.cnblogs.com/homesea/articles/10047324.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容