教育行業(yè)A股IPO第一股(股票代碼 003032)

全國咨詢/投訴熱線:400-618-4000

Ribbon的常用負載均衡策略詳細分析【技術干貨】

更新時間:2021年03月26日13時53分 來源:傳智教育 瀏覽次數(shù):

好口碑IT培訓



1.Ribbon介紹

因為微服務是目前互聯(lián)網公司比較流行的架構,所以spring就提供了一個頂級框架-spring cloud,來解決我們在開發(fā)微服務架構中遇到的各種各樣的問題,今天的主角是spring cloud 框架中集成的組件Ribbon,那么Ribbon能解決什么問題呢,我們來思考下面的問題。

微服務架構中的每個服務為了高可用,很大程度上都會進行集群,我們假設現(xiàn)在集群了3個user服務,同時能提供相同的服務,問題來了,我們如何決定調用這3個user服務中的哪一個呢?

根據(jù)不同分析角度,會有不同的答案,也可以理解為根據(jù)不同的情況,我們可以寫不同的算法,來決定到底此時此刻,調用這3個user服務的哪一個,那么,Ribbon就給我們提供了不同的算法,我們可以根據(jù)業(yè)務場景,調整配置文件,決定到底使用哪個算法,這樣,算法中就會計算出調用哪個user服務了。

2.準備工作

1)我們準備一個eureka注冊中心

2)再準備一個order服務

3)再準備3個相同代碼的user服務,這樣,order服務通過eureka注冊中心,就可以發(fā)現(xiàn)user的3個服務

3.Ribbon的常用負載均衡策略

Ribbon是通過IRule的這個接口來選擇3個user服務中的哪個的,但是實際執(zhí)行的代碼肯定是繼承了這個接口的實現(xiàn)類,所以選擇不同的實現(xiàn)類,就會選擇不同負載均衡策略

public interface IRule {
    
    Server choose(Object var1);

    void setLoadBalancer(ILoadBalancer var1);

    ILoadBalancer getLoadBalancer();
}

3.1. RoundRobinRule  輪詢策略

此策略是Ribbon的默認策略,是按照順序,依次對所有的user服務進行訪問。

通過重寫IRule的choose方法,來選擇并返回決定調用的user服務,在下面的源碼中,List allServers = lb.getAllServers(); 獲得了所有的3個user服務實例,int nextServerIndex = this.incrementAndGetModulo(serverCount); 保存了當前調用的user實例的序號,然后就可以按照順序調用下一個user服務了

public Server choose(ILoadBalancer lb, Object key) {
    if (lb == null) {
        log.warn("no load balancer");
        return null;
    } else {
        Server server = null;
        int count = 0;

        while(true) {
            if (server == null && count++ < 10) {
                List<Server> reachableServers = lb.getReachableServers();
                List<Server> allServers = lb.getAllServers();
                int upCount = reachableServers.size();
                //總服務實例數(shù)量
                int serverCount = allServers.size();
                if (upCount != 0 && serverCount != 0) {
                    int nextServerIndex = this.incrementAndGetModulo(serverCount);
                    server = (Server)allServers.get(nextServerIndex);
                    if (server == null) {
                        Thread.yield();
                    } else {
                        if (server.isAlive() && server.isReadyToServe()) {
                            return server;
                        }

                        server = null;
                    }
                    continue;
                }

                log.warn("No up servers available from load balancer: " + lb);
                return null;
            }

            if (count >= 10) {
                log.warn("No available alive servers after 10 tries from load balancer: " + lb);
            }

            return server;
        }
    }
}

debug的圖例:

第一次訪問:訪問的是第一個實例

1616729974058_01.png

第二次訪問:訪問的是第二個實例

1616730000374_02.png

3.2. RoundRobinRule  隨機策略

就和這個策略的名字一樣,是對user的3個服務的隨機調用,所以不存在規(guī)律,如下源碼中int index = this.chooseRandomInt(serverCount); 通過隨機數(shù)來選擇下標,所以對user服務的調用是隨機的

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);
            server = (Server)upList.get(index);
            if (server == null) {
                Thread.yield();
            } else {
                if (server.isAlive()) {
                    return server;
                }

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

        return server;
    }
}

debug的圖例:

第一次訪問:訪問的是第一個實例

1616730026639_03.png

第二次訪問:訪問的還是第一個實例

1616730049712_04.png

第三次訪問:訪問的是第三個實例

undefined

3.3. WeightedResponseTimeRule響應時間加權重策略

根據(jù)user的3個服務的響應時間來分配權重,響應時間越長的服務,權重越低,那么被調用的概率也就越低。相反,響應時間越短的服務,權重越高,被調用的概率也就越高

響應時間加權重策略的實現(xiàn)分為兩步:

  1. WeightedResponseTimeRule實現(xiàn)類中默認情況下每隔30秒會統(tǒng)計一次每個服務的權重,在此30秒內,用的是輪詢策略
  2. 30秒之后,會根據(jù)統(tǒng)計的結果來分配每個實例的權重,然后根據(jù)權重來分配調用次數(shù)
extends RoundRobinRule
public Server choose(ILoadBalancer lb, Object key) {
    if (lb == null) {
        return null;
    } else {
        Server server = null;

        while(server == null) {
            List<Double> currentWeights = this.accumulatedWeights;
            if (Thread.interrupted()) {
                return null;
            }

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

            int serverIndex = 0;
            double maxTotalWeight = currentWeights.size() == 0 ? 0.0D : (Double)currentWeights.get(currentWeights.size() - 1);
            //在30秒之內,maxTotalWeight變量會一直是0.0
            if (maxTotalWeight >= 0.001D && serverCount == currentWeights.size()) {
                double randomWeight = this.random.nextDouble() * maxTotalWeight;
                int n = 0;

                for(Iterator var13 = currentWeights.iterator(); var13.hasNext(); ++n) {
                    Double d = (Double)var13.next();
                    if (d >= randomWeight) {
                        serverIndex = n;
                        break;
                    }
                }

                server = (Server)allList.get(serverIndex);
            } else {
                server = super.choose(this.getLoadBalancer(), key);
                if (server == null) {
                    return server;
                }
            }

            if (server == null) {
                Thread.yield();
            } else {
                if (server.isAlive()) {
                    return server;
                }

                server = null;
            }
        }

        return server;
    }
}

debug的圖例:

前幾次訪問:maxTotalWeight都是0.0,使用輪詢策略,但是開始緩存權重數(shù)據(jù)

1616730100101_06.png

30秒之后:開始根據(jù)權重數(shù)據(jù)來分配權重,選擇實例

1616730118844_07.png

如下圖:8081端口的權重顯然沒有8082的權重大,所以8082端口的user服務實例被訪問的次數(shù)多

1616730136639_08.png

1616730143541_09.png

3.4. RetryRule 重試策略

重試策略是指通過輪詢策略選出一個實例,然后去訪問,如果此實例為null或者已經失效,那么會重試其他的實例,answer = this.subRule.choose(key); 會根據(jù)輪詢策略選擇一個實例,然后if ((answer == null || !answer.isAlive()) && System.currentTimeMillis() < deadline)判斷如果實例為null或者失效,那么會重新選擇

public Server choose(ILoadBalancer lb, Object key) {
    long requestTime = System.currentTimeMillis();
    long deadline = requestTime + this.maxRetryMillis;
    Server answer = null;
    answer = this.subRule.choose(key);
    if ((answer == null || !answer.isAlive()) && System.currentTimeMillis() < deadline) {
        InterruptTask task = new InterruptTask(deadline - System.currentTimeMillis());

        while(!Thread.interrupted()) {
            answer = this.subRule.choose(key);
            if (answer != null && answer.isAlive() || System.currentTimeMillis() >= deadline) {
                break;
            }

            Thread.yield();
        }

        task.cancel();
    }

    return answer != null && answer.isAlive() ? answer : null;
}

1616730163380_10.png

3.5. BestAvailableRule 最低并發(fā)策略

會根據(jù)每個服務實例的并發(fā)數(shù)量來決定,訪問并發(fā)數(shù)最少的那個服務,int concurrentConnections = serverStats.getActiveRequestsCount(currentTime); 會獲得當前遍歷的實例的并發(fā)數(shù),然后和其他的實例的并發(fā)數(shù)進行判斷,最終訪問并發(fā)量最少的那個實例

public Server choose(Object key) {
    if (this.loadBalancerStats == null) {
        return super.choose(key);
    } else {
        List<Server> serverList = this.getLoadBalancer().getAllServers();
        int minimalConcurrentConnections = 2147483647;
        long currentTime = System.currentTimeMillis();
        Server chosen = null;
        Iterator var7 = serverList.iterator();

        while(var7.hasNext()) { //遍歷所有的實例
            Server server = (Server)var7.next();
            ServerStats serverStats = this.loadBalancerStats.getSingleServerStat(server);
            if (!serverStats.isCircuitBreakerTripped(currentTime)) {
                int concurrentConnections = serverStats.getActiveRequestsCount(currentTime); //判斷并發(fā)數(shù),并和已經判斷出的最少的并發(fā)數(shù)比較
                if (concurrentConnections < minimalConcurrentConnections) {
                    minimalConcurrentConnections = concurrentConnections;
                    chosen = server;
                }
            }
        }

        if (chosen == null) {
            return super.choose(key);
        } else {
            return chosen;
        }
    }
}

3.6. AvailabilityFilteringRule 可用過濾策略

此策略會聰明的過濾掉一直失敗并被標記為circuit tripped的user服務,而且會過濾掉那些高并發(fā)的user服務

public Server choose(Object key) {
    int count = 0;
    
    for(Server server = this.roundRobinRule.choose(key); count++ <= 10; server = this.roundRobinRule.choose(key)) {
        //通過predicate來過濾
        if (this.predicate.apply(new PredicateKey(server))) {
            return server;
        }
    }
    //過濾掉一些服務之后,會采用輪詢的方式調用剩下的服務
    return super.choose(key);
}

3.7. ClientConfigEnabledRoundRobinRule 自定義策略

此策略本身并沒有實現(xiàn)什么特殊的處理邏輯,但是可以通過重置LoadBalancer來達到自定義一些高級策略的目的,可以重寫initWithNiwsConfig和setLoadBalancer

public void initWithNiwsConfig(IClientConfig clientConfig) {
    this.roundRobinRule = new RoundRobinRule();
}

public void setLoadBalancer(ILoadBalancer lb) {
    super.setLoadBalancer(lb);
    this.roundRobinRule.setLoadBalancer(lb);
}

public Server choose(Object key) {
    if (this.roundRobinRule != null) {
        return this.roundRobinRule.choose(key);
    } else {
        throw new IllegalArgumentException("This class has not been initialized with the RoundRobinRule class");
    }
}


猜你喜歡:

Ribbon工作原理詳細介紹

Zookeeper是如何選取主leader的?

Spring Cloud是什么?怎么理解Spring Cloud?

Spring Boot如何實現(xiàn)微服務?/a>

Spring框架功能分為哪些模塊?

傳智教育java培訓課程

0 分享到:
和我們在線交談!